1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "rewriter/calculator_rewriter.h"
31
32 #include <algorithm>
33 #include <string>
34
35 #include "base/logging.h"
36 #include "base/util.h"
37 #include "config/config_handler.h"
38 #include "converter/converter_interface.h"
39 #include "converter/segments.h"
40 #include "protocol/commands.pb.h"
41 #include "protocol/config.pb.h"
42 #include "request/conversion_request.h"
43 #include "rewriter/calculator/calculator_interface.h"
44
45 namespace mozc {
46
CalculatorRewriter(const ConverterInterface * parent_converter)47 CalculatorRewriter::CalculatorRewriter(
48 const ConverterInterface *parent_converter)
49 : parent_converter_(parent_converter) {
50 DCHECK(parent_converter_);
51 }
52
~CalculatorRewriter()53 CalculatorRewriter::~CalculatorRewriter() {}
54
capability(const ConversionRequest & request) const55 int CalculatorRewriter::capability(const ConversionRequest &request) const {
56 if (request.request().mixed_conversion()) {
57 return RewriterInterface::ALL;
58 }
59 return RewriterInterface::CONVERSION;
60 }
61
62 // Rewrites candidates when conversion segments of |segments| represents an
63 // expression that can be calculated. In such case, if |segments| consists
64 // of multiple segments, it merges them by calling ConverterInterface::
65 // ResizeSegment(), otherwise do calculation and insertion.
66 // TODO(tok): It currently calculates same expression twice, if |segments| is
67 // a valid expression.
Rewrite(const ConversionRequest & request,Segments * segments) const68 bool CalculatorRewriter::Rewrite(const ConversionRequest &request,
69 Segments *segments) const {
70 if (!request.config().use_calculator()) {
71 return false;
72 }
73
74 CalculatorInterface *calculator = CalculatorFactory::GetCalculator();
75
76 const size_t segments_size = segments->conversion_segments_size();
77 if (segments_size == 0) {
78 return false;
79 }
80
81 // If |segments| has only one conversion segment, try calculation and insert
82 // the result on success.
83 if (segments_size == 1) {
84 const string &key = segments->conversion_segment(0).key();
85 string result;
86 if (key.empty()) {
87 return false;
88 }
89 if (!calculator->CalculateString(key, &result)) {
90 return false;
91 }
92 // Insert the result.
93 if (!InsertCandidate(result, 0, segments->mutable_conversion_segment(0))) {
94 return false;
95 }
96 return true;
97 }
98
99 // Merge keys of all conversion segments and try calculation.
100 string merged_key;
101 for (size_t i = 0; i < segments->conversion_segments_size(); ++i) {
102 merged_key += segments->conversion_segment(i).key();
103 }
104 // The decision to calculate and calculation itself are both done by the
105 // calculator.
106 string result;
107 if (!calculator->CalculateString(merged_key, &result)) {
108 return false;
109 }
110
111 // Merge all conversion segments.
112 int offset = Util::CharsLen(merged_key) -
113 Util::CharsLen(segments->conversion_segment(0).key());
114 // ConverterInterface::ResizeSegment() calls Rewriter::Rewrite(), so
115 // CalculatorRewriter::Rewrite() is recursively called with merged
116 // conversion segment.
117 if (!parent_converter_->ResizeSegment(segments, request, 0, offset)) {
118 LOG(ERROR) << "Failed to merge conversion segments";
119 return false;
120 }
121 return true;
122 }
123
InsertCandidate(const string & value,size_t insert_pos,Segment * segment) const124 bool CalculatorRewriter::InsertCandidate(const string &value,
125 size_t insert_pos,
126 Segment *segment) const {
127 if (segment->candidates_size() == 0) {
128 LOG(WARNING) << "candidates_size is 0";
129 return false;
130 }
131
132 const Segment::Candidate &base_candidate = segment->candidate(0);
133
134 // Normalize the expression, used in description.
135 string temp, temp2, expression;
136 Util::FullWidthAsciiToHalfWidthAscii(base_candidate.content_key, &temp);
137 Util::StringReplace(temp, "・", "/", true, &temp2);
138 // "ー", onbiki
139 Util::StringReplace(temp2, "ー", "-", true, &expression);
140
141 size_t offset = std::min(insert_pos, segment->candidates_size());
142
143 for (int n = 0; n < 2; ++n) {
144 int current_offset = offset + n;
145 Segment::Candidate *candidate = segment->insert_candidate(
146 current_offset);
147 if (candidate == NULL) {
148 LOG(ERROR) << "cannot insert candidate at " << current_offset;
149 return false;
150 }
151
152 // Simply sets some member variables of the new candidate to ones of the
153 // existing candidate next to it.
154 size_t reference_index = current_offset + 1;
155 if (reference_index >= segment->candidates_size()) {
156 reference_index = current_offset - 1;
157 }
158 const Segment::Candidate &reference_candidate =
159 segment->candidate(reference_index);
160
161 candidate->Init();
162 candidate->lid = reference_candidate.lid;
163 candidate->rid = reference_candidate.rid;
164 candidate->cost = reference_candidate.cost;
165 candidate->key = base_candidate.key;
166 candidate->content_key = base_candidate.content_key;
167 candidate->attributes |= Segment::Candidate::NO_VARIANTS_EXPANSION;
168 candidate->attributes |= Segment::Candidate::NO_LEARNING;
169 candidate->description = "計算結果";
170
171 if (n == 0) { // without expression
172 candidate->value = value;
173 candidate->content_value = value;
174 } else { // with expression
175 DCHECK(!expression.empty());
176 if (expression.front() == '=') {
177 // Expression starts with '='.
178 // Appends value to the left of expression.
179 candidate->value = value + expression;
180 candidate->content_value = value + expression;
181 } else {
182 // Expression ends with '='.
183 // Appends value to the right of expression.
184 candidate->value = expression + value;
185 candidate->content_value = expression + value;
186 }
187 }
188 }
189
190 return true;
191 }
192 } // namespace mozc
193