// Copyright 2010-2020, 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 "rewriter/command_rewriter.h"
#include <algorithm>
#include <string>
#include <vector>
#include "base/logging.h"
#include "config/config_handler.h"
#include "converter/segments.h"
#include "protocol/config.pb.h"
#include "request/conversion_request.h"
namespace mozc {
namespace {
const char kPrefix[] = "【";
const char kSuffix[] = "】";
const char kDescription[] = "設定を変更します";
// Trigger CommandRewriter if and only if the Segment::key is one of
// kTriggerKeys[]
const char *kTriggerKeys[] = {
"こまんど", "しーくれっと", "しーくれっともーど", "ひみつ",
"ぷらいばしー", "ぷらいべーと", "さじぇすと", "ぷれぜんてーしょん",
"ぷれぜん", "よそく", "よそくにゅうりょく", "よそくへんかん",
"すいそくこうほ"};
// Trigger Values for all commands
const char *kCommandValues[] = {"コマンド"};
// Trigger Values for Incoginito Mode.
const char *kIncognitoModeValues[] = {"秘密", "シークレット",
"シークレットモード", "プライバシー",
"プライベート"};
const char *kDisableAllSuggestionValues[] = {"サジェスト", "予測",
"予測入力", "予測変換",
"プレゼンテーション", "プレゼン"};
const char kIncoginitoModeOn[] = "シークレットモードをオン";
const char kIncoginitoModeOff[] = "シークレットモードをオフ";
const char kDisableAllSuggestionOn[] = "サジェスト機能の一時停止";
const char kDisableAllSuggestionOff[] = "サジェスト機能を元に戻す";
bool FindString(const string &query, const char **values, size_t size) {
DCHECK(values);
DCHECK_GT(size, 0);
for (size_t i = 0; i < size; ++i) {
if (query == values[i]) {
return true;
}
}
return false;
}
Segment::Candidate *InsertCommandCandidate(Segment *segment,
size_t reference_pos,
size_t insert_pos) {
DCHECK(segment);
Segment::Candidate *candidate = segment->insert_candidate(
std::min(segment->candidates_size(), insert_pos));
DCHECK(candidate);
candidate->CopyFrom(segment->candidate(reference_pos));
candidate->attributes |= Segment::Candidate::COMMAND_CANDIDATE;
candidate->attributes |= Segment::Candidate::NO_LEARNING;
candidate->description = kDescription;
candidate->prefix = kPrefix;
candidate->suffix = kSuffix;
candidate->inner_segment_boundary.clear();
DCHECK(candidate->IsValid());
return candidate;
}
bool IsSuggestionEnabled(const config::Config &config) {
return config.use_history_suggest() || config.use_dictionary_suggest() ||
config.use_realtime_conversion();
}
} // namespace
CommandRewriter::CommandRewriter() {}
CommandRewriter::~CommandRewriter() {}
void CommandRewriter::InsertIncognitoModeToggleCommand(
const config::Config &config, Segment *segment, size_t reference_pos,
size_t insert_pos) const {
Segment::Candidate *candidate =
InsertCommandCandidate(segment, reference_pos, insert_pos);
DCHECK(candidate);
if (config.incognito_mode()) {
candidate->value = kIncoginitoModeOff;
candidate->command = Segment::Candidate::DISABLE_INCOGNITO_MODE;
} else {
candidate->value = kIncoginitoModeOn;
candidate->command = Segment::Candidate::ENABLE_INCOGNITO_MODE;
}
candidate->content_value = candidate->value;
}
void CommandRewriter::InsertDisableAllSuggestionToggleCommand(
const config::Config &config, Segment *segment, size_t reference_pos,
size_t insert_pos) const {
if (!IsSuggestionEnabled(config)) {
return;
}
Segment::Candidate *candidate =
InsertCommandCandidate(segment, reference_pos, insert_pos);
DCHECK(candidate);
if (config.presentation_mode()) {
candidate->value = kDisableAllSuggestionOff;
candidate->command = Segment::Candidate::DISABLE_PRESENTATION_MODE;
} else {
candidate->value = kDisableAllSuggestionOn;
candidate->command = Segment::Candidate::ENABLE_PRESENTATION_MODE;
}
candidate->content_value = candidate->value;
}
bool CommandRewriter::RewriteSegment(const config::Config &config,
Segment *segment) const {
DCHECK(segment);
for (size_t i = 0; i < segment->candidates_size(); ++i) {
const string &value = segment->candidate(i).value;
if (FindString(value, kCommandValues, arraysize(kCommandValues))) {
// insert command candidate at an fixed position.
InsertDisableAllSuggestionToggleCommand(config, segment, i, 6);
InsertIncognitoModeToggleCommand(config, segment, i, 6);
return true;
}
if (FindString(value, kIncognitoModeValues,
arraysize(kIncognitoModeValues))) {
InsertIncognitoModeToggleCommand(config, segment, i, i + 3);
return true;
}
if (FindString(value, kDisableAllSuggestionValues,
arraysize(kDisableAllSuggestionValues))) {
InsertDisableAllSuggestionToggleCommand(config, segment, i, i + 3);
return true;
}
}
return false;
}
bool CommandRewriter::Rewrite(const ConversionRequest &request,
Segments *segments) const {
if (segments == nullptr || segments->conversion_segments_size() != 1) {
return false;
}
Segment *segment = segments->mutable_conversion_segment(0);
DCHECK(segment);
const string &key = segment->key();
// TODO(taku): we want to replace the linear search with STL map when
// kTriggerKeys become bigger.
if (!FindString(key, kTriggerKeys, arraysize(kTriggerKeys))) {
return false;
}
return RewriteSegment(request.config(), segment);
}
} // namespace mozc