| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <iostream> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "base/file_stream.h" |
| #include "base/logging.h" |
| #include "base/number_util.h" |
| #include "base/port.h" |
| #include "base/singleton.h" |
| #include "base/system_util.h" |
| #include "base/util.h" |
| #include "composer/composer.h" |
| #include "composer/table.h" |
| #include "config/config.pb.h" |
| #include "converter/conversion_request.h" |
| #include "converter/converter_interface.h" |
| #include "converter/lattice.h" |
| #include "converter/pos_id_printer.h" |
| #include "converter/segments.h" |
| #include "engine/engine_factory.h" |
| #include "engine/engine_interface.h" |
| #include "engine/mock_data_engine_factory.h" |
| #include "session/commands.pb.h" |
| |
| DEFINE_int32(max_conversion_candidates_size, 200, "maximum candidates size"); |
| DEFINE_string(user_profile_dir, "", "path to user profile directory"); |
| DEFINE_string(engine, "default", "engine: (default, chromeos)"); |
| DEFINE_bool(output_debug_string, true, "output debug string for each input"); |
| DEFINE_bool(show_meta_candidates, false, "if true, show meta candidates"); |
| DEFINE_string( |
| id_def, |
| "", |
| "id.def file for POS IDs. If provided, show human readable " |
| "POS instead of ID number"); |
| |
| using mozc::composer::Composer; |
| using mozc::composer::Table; |
| using mozc::config::Config; |
| |
| namespace mozc { |
| namespace { |
| |
| // Wrapper class for pos id printing |
| class PosIdPrintUtil { |
| public: |
| static string IdToString(int id) { |
| return Singleton<PosIdPrintUtil>::get()->IdToStringInternal(id); |
| } |
| |
| private: |
| PosIdPrintUtil() : |
| pos_id_(new InputFileStream(FLAGS_id_def.c_str())), |
| pos_id_printer_(new internal::PosIdPrinter(pos_id_.get())) {} |
| |
| string IdToStringInternal(int id) const { |
| const string &pos_string = pos_id_printer_->IdToString(id); |
| if (pos_string.empty()) { |
| return NumberUtil::SimpleItoa(id); |
| } |
| return Util::StringPrintf("%s (%d)", pos_string.c_str(), id); |
| } |
| |
| scoped_ptr<InputFileStream> pos_id_; |
| scoped_ptr<internal::PosIdPrinter> pos_id_printer_; |
| |
| friend class Singleton<PosIdPrintUtil>; |
| DISALLOW_COPY_AND_ASSIGN(PosIdPrintUtil); |
| }; |
| |
| string SegmentTypeToString(Segment::SegmentType type) { |
| #define RETURN_STR(val) case Segment::val: return #val |
| switch (type) { |
| RETURN_STR(FREE); |
| RETURN_STR(FIXED_BOUNDARY); |
| RETURN_STR(FIXED_VALUE); |
| RETURN_STR(SUBMITTED); |
| RETURN_STR(HISTORY); |
| default: |
| return "UNKNOWN"; |
| } |
| #undef RETURN_STR |
| } |
| |
| string CandidateAttributesToString(uint32 attrs) { |
| vector<string> v; |
| #define ADD_STR(fieldname) \ |
| do { \ |
| if (attrs & Segment::Candidate::fieldname) \ |
| v.push_back(#fieldname); \ |
| } while (false) |
| |
| ADD_STR(BEST_CANDIDATE); |
| ADD_STR(RERANKED); |
| ADD_STR(NO_HISTORY_LEARNING); |
| ADD_STR(NO_SUGGEST_LEARNING); |
| ADD_STR(CONTEXT_SENSITIVE); |
| ADD_STR(SPELLING_CORRECTION); |
| ADD_STR(NO_VARIANTS_EXPANSION); |
| ADD_STR(NO_EXTRA_DESCRIPTION); |
| ADD_STR(REALTIME_CONVERSION); |
| ADD_STR(USER_DICTIONARY); |
| ADD_STR(COMMAND_CANDIDATE); |
| ADD_STR(PARTIALLY_KEY_CONSUMED); |
| ADD_STR(TYPING_CORRECTION); |
| ADD_STR(AUTO_PARTIAL_SUGGESTION); |
| ADD_STR(USER_HISTORY_PREDICTION); |
| |
| #undef ADD_STR |
| string s; |
| Util::JoinStrings(v, " | ", &s); |
| return s; |
| } |
| |
| string NumberStyleToString(NumberUtil::NumberString::Style style) { |
| #define RETURN_STR(val) case NumberUtil::NumberString::val: return #val |
| switch (style) { |
| RETURN_STR(DEFAULT_STYLE); |
| RETURN_STR(NUMBER_SEPARATED_ARABIC_HALFWIDTH); |
| RETURN_STR(NUMBER_SEPARATED_ARABIC_FULLWIDTH); |
| RETURN_STR(NUMBER_ARABIC_AND_KANJI_HALFWIDTH); |
| RETURN_STR(NUMBER_ARABIC_AND_KANJI_FULLWIDTH); |
| RETURN_STR(NUMBER_KANJI); |
| RETURN_STR(NUMBER_OLD_KANJI); |
| RETURN_STR(NUMBER_ROMAN_CAPITAL); |
| RETURN_STR(NUMBER_ROMAN_SMALL); |
| RETURN_STR(NUMBER_CIRCLED); |
| RETURN_STR(NUMBER_KANJI_ARABIC); |
| RETURN_STR(NUMBER_HEX); |
| RETURN_STR(NUMBER_OCT); |
| RETURN_STR(NUMBER_BIN); |
| default: |
| return "UNKNOWN"; |
| } |
| #undef RETURN_STR |
| } |
| |
| string InnerSegmentBoundaryToString(const Segment::Candidate &cand) { |
| if (cand.inner_segment_boundary.empty()) { |
| return ""; |
| } |
| vector<string> pieces; |
| for (Segment::Candidate::InnerSegmentIterator iter(&cand); |
| !iter.Done(); iter.Next()) { |
| string s = "<"; |
| iter.GetKey().AppendToString(&s); |
| s.append(", "); |
| iter.GetValue().AppendToString(&s); |
| s.append(", "); |
| iter.GetContentKey().AppendToString(&s); |
| s.append(", "); |
| iter.GetContentValue().AppendToString(&s); |
| s.append(1, '>'); |
| pieces.push_back(s); |
| } |
| string s; |
| Util::JoinStrings(pieces, " | ", &s); |
| return s; |
| } |
| |
| void PrintCandidate(const Segment &parent, int num, |
| const Segment::Candidate &cand, ostream *os) { |
| vector<string> lines; |
| if (parent.key() != cand.key) { |
| lines.push_back("key: " + cand.key); |
| } |
| lines.push_back("content_vk: " + cand.content_value + |
| " " + cand.content_key); |
| lines.push_back(Util::StringPrintf( |
| "cost: %d scost: %d wcost: %d", |
| cand.cost, cand.structure_cost, cand.wcost)); |
| lines.push_back("lid: " + PosIdPrintUtil::IdToString(cand.lid)); |
| lines.push_back("rid: " + PosIdPrintUtil::IdToString(cand.rid)); |
| lines.push_back("attr: " + CandidateAttributesToString(cand.attributes)); |
| lines.push_back("num_style: " + NumberStyleToString(cand.style)); |
| const string &segbdd_str = InnerSegmentBoundaryToString(cand); |
| if (!segbdd_str.empty()) { |
| lines.push_back("segbdd: " + segbdd_str); |
| } |
| |
| (*os) << " " << num << " " << cand.value << endl; |
| for (size_t i = 0; i < lines.size(); ++i) { |
| if (!lines[i].empty()) { |
| (*os) << " " << lines[i] << endl; |
| } |
| } |
| } |
| |
| void PrintSegment(size_t num, size_t segments_size, |
| const Segment &segment, ostream *os) { |
| (*os) << "---------- Segment " << num << "/" << segments_size << " [" |
| << SegmentTypeToString(segment.segment_type()) |
| << "] ----------" << endl |
| << segment.key() << endl; |
| if (FLAGS_show_meta_candidates) { |
| for (int i = 0; i < segment.meta_candidates_size(); ++i) { |
| PrintCandidate(segment, -i - 1, segment.meta_candidate(i), os); |
| } |
| } |
| for (size_t i = 0; i < segment.candidates_size(); ++i) { |
| PrintCandidate(segment, i, segment.candidate(i), os); |
| } |
| } |
| |
| void PrintSegments(const Segments &segments, ostream *os) { |
| for (size_t i = 0; i < segments.segments_size(); ++i) { |
| PrintSegment(i, segments.segments_size(), segments.segment(i), os); |
| } |
| } |
| |
| bool ExecCommand(const ConverterInterface &converter, |
| Segments *segments, |
| const string &line, |
| const commands::Request &request) { |
| vector<string> fields; |
| Util::SplitStringUsing(line, "\t ", &fields); |
| |
| #define CHECK_FIELDS_LENGTH(length) \ |
| if (fields.size() < (length)) { \ |
| return false; \ |
| } |
| |
| CHECK_FIELDS_LENGTH(1); |
| |
| const string &func = fields[0]; |
| |
| const Config config; |
| |
| segments->set_max_conversion_candidates_size( |
| FLAGS_max_conversion_candidates_size); |
| |
| if (func == "startconversion" || func == "start" || func == "s") { |
| CHECK_FIELDS_LENGTH(2); |
| Table table; |
| Composer composer(&table, &request); |
| composer.InsertCharacterPreedit(fields[1]); |
| ConversionRequest conversion_request(&composer, &request); |
| return converter.StartConversionForRequest(conversion_request, segments); |
| } else if (func == "convertwithnodeinfo" || func == "cn") { |
| CHECK_FIELDS_LENGTH(5); |
| Lattice::SetDebugDisplayNode(atoi32(fields[2].c_str()), // begin pos |
| atoi32(fields[3].c_str()), // end pos |
| fields[4]); |
| const bool result = converter.StartConversion(segments, fields[1]); |
| Lattice::ResetDebugDisplayNode(); |
| return result; |
| } else if (func == "reverseconversion" || func == "reverse" || func == "r") { |
| CHECK_FIELDS_LENGTH(2); |
| return converter.StartReverseConversion(segments, fields[1]); |
| } else if (func == "startprediction" || func == "predict" || func == "p") { |
| Table table; |
| Composer composer(&table, &request); |
| if (fields.size() >= 2) { |
| composer.InsertCharacterPreedit(fields[1]); |
| ConversionRequest conversion_request(&composer, &request); |
| return converter.StartPredictionForRequest(conversion_request, segments); |
| } else { |
| ConversionRequest conversion_request(&composer, &request); |
| return converter.StartPredictionForRequest(conversion_request, segments); |
| } |
| } else if (func == "startsuggestion" || func == "suggest") { |
| Table table; |
| Composer composer(&table, &request); |
| if (fields.size() >= 2) { |
| composer.InsertCharacterPreedit(fields[1]); |
| ConversionRequest conversion_request(&composer, &request); |
| return converter.StartSuggestionForRequest(conversion_request, segments); |
| } else { |
| ConversionRequest conversion_request(&composer, &request); |
| return converter.StartSuggestionForRequest(conversion_request, segments); |
| } |
| } else if (func == "finishconversion" || func == "finish") { |
| Table table; |
| Composer composer(&table, &request); |
| ConversionRequest conversion_request(&composer, &request); |
| return converter.FinishConversion(conversion_request, segments); |
| } else if (func == "resetconversion" || func == "reset") { |
| return converter.ResetConversion(segments); |
| } else if (func == "cancelconversion" || func == "cancel") { |
| return converter.CancelConversion(segments); |
| } else if (func == "commitsegmentvalue" || func == "commit" || func == "c") { |
| CHECK_FIELDS_LENGTH(3); |
| return converter.CommitSegmentValue(segments, |
| atoi32(fields[1].c_str()), |
| atoi32(fields[2].c_str())); |
| } else if (func == "commitallandfinish") { |
| for (int i = 0; i < segments->conversion_segments_size(); ++i) { |
| if (segments->conversion_segment(i).segment_type() != |
| Segment::FIXED_VALUE) { |
| if (!(converter.CommitSegmentValue(segments, i, 0))) return false; |
| } |
| } |
| Table table; |
| Composer composer(&table, &request); |
| ConversionRequest conversion_request(&composer, &request); |
| return converter.FinishConversion(conversion_request, segments); |
| } else if (func == "focussegmentvalue" || func == "focus") { |
| CHECK_FIELDS_LENGTH(3); |
| return converter.FocusSegmentValue(segments, |
| atoi32(fields[1].c_str()), |
| atoi32(fields[2].c_str())); |
| } else if (func == "commitfirstsegment") { |
| CHECK_FIELDS_LENGTH(2); |
| vector<size_t> singleton_vector; |
| singleton_vector.push_back(static_cast<size_t>(atoi32(fields[1].c_str()))); |
| return converter.CommitSegments(segments, singleton_vector); |
| } else if (func == "freesegmentvalue" || func == "free") { |
| CHECK_FIELDS_LENGTH(2); |
| return converter.FreeSegmentValue(segments, |
| atoi32(fields[1].c_str())); |
| } else if (func == "resizesegment" || func == "resize") { |
| const ConversionRequest request; |
| if (fields.size() == 3) { |
| return converter.ResizeSegment(segments, |
| request, |
| atoi32(fields[1].c_str()), |
| atoi32(fields[2].c_str())); |
| } else if (fields.size() > 3) { |
| vector<uint8> new_arrays; |
| for (size_t i = 3; i < fields.size(); ++i) { |
| new_arrays.push_back(static_cast<uint8>(atoi32(fields[i].c_str()))); |
| } |
| return converter.ResizeSegment(segments, |
| request, |
| atoi32(fields[1].c_str()), // start |
| atoi32(fields[2].c_str()), |
| &new_arrays[0], |
| new_arrays.size()); |
| } |
| } else if (func == "disableuserhistory") { |
| segments->set_user_history_enabled(false); |
| } else if (func == "enableuserhistory") { |
| segments->set_user_history_enabled(true); |
| } else { |
| LOG(WARNING) << "Unknown command: " << func; |
| return false; |
| } |
| |
| #undef CHECK_FIELDS_LENGTH |
| return true; |
| } |
| |
| } // namespace |
| } // namespace mozc |
| |
| int main(int argc, char **argv) { |
| InitGoogle(argv[0], &argc, &argv, false); |
| |
| if (!FLAGS_user_profile_dir.empty()) { |
| mozc::SystemUtil::SetUserProfileDirectory(FLAGS_user_profile_dir); |
| } |
| |
| scoped_ptr<mozc::EngineInterface> engine; |
| mozc::commands::Request request; |
| if (FLAGS_engine == "default") { |
| LOG(INFO) << "Using default preference and engine"; |
| engine.reset(mozc::EngineFactory::Create()); |
| } |
| if (FLAGS_engine == "test") { |
| LOG(INFO) << "Using test preference and engine"; |
| engine.reset(mozc::MockDataEngineFactory::Create()); |
| } |
| |
| CHECK(engine.get()) << "Invalid engine: " << FLAGS_engine; |
| mozc::ConverterInterface *converter = engine->GetConverter(); |
| CHECK(converter); |
| |
| mozc::Segments segments; |
| string line; |
| |
| while (!getline(cin, line).fail()) { |
| if (mozc::ExecCommand(*converter, &segments, line, request)) { |
| if (FLAGS_output_debug_string) { |
| mozc::PrintSegments(segments, &cout); |
| } |
| } else { |
| cout << "ExecCommand() return false" << endl; |
| } |
| } |
| return 0; |
| } |