| // 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 "rewriter/fortune_rewriter.h" |
| |
| #include <algorithm> |
| #include <ctime> |
| #include <string> |
| |
| #include "base/logging.h" |
| #include "base/singleton.h" |
| #include "base/util.h" |
| #include "converter/conversion_request.h" |
| #include "converter/segments.h" |
| #include "rewriter/rewriter_interface.h" |
| |
| namespace mozc { |
| namespace { |
| |
| enum FortuneType { |
| FORTUNE_TYPE_EXCELLENT_LUCK = 0, |
| FORTUNE_TYPE_LUCK = 1, |
| FORTUNE_TYPE_MIDDLE_LUCK = 2, |
| FORTUNE_TYPE_LITTLE_LUCK = 3, |
| FORTUNE_TYPE_LUCK_AT_THE_END = 4, |
| FORTUNE_TYPE_MISFORTUNE = 5, |
| NUM_FORTUNE_TYPES = 6, |
| }; |
| |
| const int kMaxLevel = 100; |
| const int kNormalLevels[] = { 20, 40, 60, 80, 90 }; |
| const int kNewYearLevels[] = { 30, 60, 80, 90, 95 }; |
| const int kMyBirthdayLevels[] = { 30, 60, 80, 90, 95 }; |
| const int kFriday13Levels[] = { 10, 25, 40, 55, 70 }; |
| |
| bool IsValidFortuneType(FortuneType fortune_type) { |
| return (fortune_type >= 0 && fortune_type < NUM_FORTUNE_TYPES); |
| } |
| |
| class FortuneData { |
| public: |
| FortuneData() |
| : fortune_type_(FORTUNE_TYPE_EXCELLENT_LUCK), |
| last_update_yday_(-1), |
| last_update_year_(-1) { |
| ChangeFortune(); |
| } |
| |
| void ChangeFortune() { |
| const int *levels = kNormalLevels; |
| tm today; |
| if (Util::GetCurrentTm(&today)) { |
| // Modify once per one day |
| if (today.tm_yday == last_update_yday_ && |
| today.tm_year == last_update_year_) { |
| return; |
| } |
| last_update_yday_ = today.tm_yday; |
| last_update_year_ = today.tm_year; |
| // More happy at New Year's Day |
| if (today.tm_yday == 0) { |
| levels = kNewYearLevels; |
| } else if (today.tm_mon == 2 && today.tm_mday == 3) { |
| // It's my birthday :) |
| levels = kMyBirthdayLevels; |
| } else if (today.tm_mday == 13 && today.tm_wday == 5) { |
| // Friday the 13th |
| levels = kFriday13Levels; |
| } |
| } |
| uint32 random = 0; |
| Util::GetRandomSequence(reinterpret_cast<char *>(&random), sizeof(random)); |
| const int level = random % kMaxLevel; |
| for (int i = 0; i < arraysize(kNormalLevels); ++i) { |
| if (level <= levels[i]) { |
| fortune_type_ = static_cast<FortuneType>(i); |
| break; |
| } |
| } |
| DCHECK(IsValidFortuneType(fortune_type_)); |
| } |
| |
| FortuneType fortune_type() const { return fortune_type_; } |
| |
| private: |
| FortuneType fortune_type_; |
| int last_update_yday_; |
| int last_update_year_; |
| }; |
| |
| // Insert Fortune message into the |segment| |
| // Only one fortune indicated by fortune_type is inserted at |
| // |insert_pos|. Return false if insersion is failed. |
| bool InsertCandidate(FortuneType fortune_type, |
| size_t insert_pos, |
| Segment *segment) { |
| if (segment->candidates_size() == 0) { |
| LOG(WARNING) << "candidates_size is 0"; |
| return false; |
| } |
| |
| const Segment::Candidate& base_candidate = segment->candidate(0); |
| size_t offset = min(insert_pos, segment->candidates_size()); |
| |
| Segment::Candidate *c = segment->insert_candidate(offset); |
| if (c == NULL) { |
| LOG(ERROR) << "cannot insert candidate at " << offset; |
| return false; |
| } |
| const Segment::Candidate &trigger_c = segment->candidate(offset - 1); |
| |
| string value; |
| switch (fortune_type) { |
| case FORTUNE_TYPE_EXCELLENT_LUCK: // "大吉" |
| value = "\xE5\xA4\xA7\xE5\x90\x89"; |
| break; |
| case FORTUNE_TYPE_LUCK: // "吉" |
| value = "\xE5\x90\x89"; |
| break; |
| case FORTUNE_TYPE_MIDDLE_LUCK: // "中吉" |
| value = "\xE4\xB8\xAD\xE5\x90\x89"; |
| break; |
| case FORTUNE_TYPE_LITTLE_LUCK: // "小吉" |
| value = "\xE5\xB0\x8F\xE5\x90\x89"; |
| break; |
| case FORTUNE_TYPE_LUCK_AT_THE_END: // "末吉" |
| value = "\xE6\x9C\xAB\xE5\x90\x89"; |
| break; |
| case FORTUNE_TYPE_MISFORTUNE: // "凶" |
| value = "\xE5\x87\xB6"; |
| break; |
| default: |
| LOG(FATAL) << "undefined fortune type"; |
| return false; |
| } |
| |
| c->Init(); |
| c->lid = trigger_c.lid; |
| c->rid = trigger_c.rid; |
| c->cost = trigger_c.cost; |
| c->value = value; |
| c->content_value = value; |
| c->key = base_candidate.key; |
| c->content_key = base_candidate.content_key; |
| c->attributes |= Segment::Candidate::NO_VARIANTS_EXPANSION; |
| c->attributes |= Segment::Candidate::NO_LEARNING; |
| // discription "今日の運勢" |
| c->description = |
| "\xE4\xBB\x8A\xE6\x97\xA5\xE3\x81\xAE\xE9\x81\x8B\xE5\x8B\xA2"; |
| return true; |
| } |
| |
| } // namespace |
| |
| FortuneRewriter::FortuneRewriter() {} |
| |
| FortuneRewriter::~FortuneRewriter() {} |
| |
| bool FortuneRewriter::Rewrite(const ConversionRequest &request, |
| Segments *segments) const { |
| if (segments->conversion_segments_size() != 1) { |
| return false; |
| } |
| |
| const Segment &segment = segments->conversion_segment(0); |
| const string &key = segment.key(); |
| if (key.empty()) { |
| LOG(ERROR) << "Key is empty"; |
| return false; |
| } |
| |
| // "おみくじ" |
| if (key != "\xE3\x81\x8A\xE3\x81\xBF\xE3\x81\x8F\xE3\x81\x98") { |
| return false; |
| } |
| FortuneData *fortune_data = Singleton<FortuneData>::get(); |
| fortune_data->ChangeFortune(); |
| // Insert a fortune candidate into the last of all candidates. |
| return InsertCandidate(fortune_data->fortune_type(), |
| segment.candidates_size(), |
| segments->mutable_conversion_segment(0)); |
| } |
| } // namespace mozc |