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 // A class handling the converter on the session layer.
31 
32 #ifndef MOZC_SESSION_SESSION_CONVERTER_H_
33 #define MOZC_SESSION_SESSION_CONVERTER_H_
34 
35 #include <memory>
36 #include <string>
37 #include <vector>
38 
39 #include "base/port.h"
40 #include "session/session_converter_interface.h"
41 
42 namespace mozc {
43 namespace commands {
44 class CandidateList;
45 class Candidates;
46 class Context;
47 class Output;
48 class Preedit;
49 class Request;
50 class Result;
51 }  // namespace commands
52 
53 namespace config {
54 class Config;
55 }  // namespace config
56 
57 namespace session {
58 class CandidateList;
59 
60 // Class handling ConverterInterface with a session state.  This class
61 // support stateful operations related with the converter.
62 class SessionConverter : public SessionConverterInterface {
63  public:
64   SessionConverter(const ConverterInterface *converter,
65                    const commands::Request *request,
66                    const config::Config *config);
67   virtual ~SessionConverter();
68 
69   // Checks if the current state is in the state bitmap.
70   virtual bool CheckState(States) const;
71 
72   // Indicates if the conversion session is active or not.  In general,
73   // Convert functions make it active and Cancel, Reset and Commit
74   // functions make it deactive.
75   virtual bool IsActive() const;
76 
77   // Returns the default conversion preferences to be used for custom
78   // conversion.
79   virtual const ConversionPreferences &conversion_preferences() const;
80 
81   // Gets the selected candidate. If no candidate is selected, returns NULL.
82   virtual const Segment::Candidate *
83   GetSelectedCandidateOfFocusedSegment() const;
84 
85   // Sends a conversion request to the converter.
86   virtual bool Convert(const composer::Composer &composer);
87   virtual bool ConvertWithPreferences(const composer::Composer &composer,
88                                       const ConversionPreferences &preferences);
89 
90   // Gets reading text (e.g. from "猫" to "ねこ").
91   virtual bool GetReadingText(const string &source_text, string *reading);
92 
93   // Sends a transliteration request to the converter.
94   virtual bool ConvertToTransliteration(
95       const composer::Composer &composer,
96       transliteration::TransliterationType type);
97 
98   // Converts the current composition to half-width characters.
99   // NOTE(komatsu): This function might be merged to ConvertToTransliteration.
100   virtual bool ConvertToHalfWidth(const composer::Composer &composer);
101 
102   // Switches the composition to Hiragana, full-width Katakana or
103   // half-width Katakana by rotation.
104   virtual bool SwitchKanaType(const composer::Composer &composer);
105 
106   // Sends a suggestion request to the converter.
107   virtual bool Suggest(const composer::Composer &composer);
108   virtual bool SuggestWithPreferences(const composer::Composer &composer,
109                                       const ConversionPreferences &preferences);
110 
111   // Sends a prediction request to the converter.
112   virtual bool Predict(const composer::Composer &composer);
113   virtual bool PredictWithPreferences(const composer::Composer &composer,
114                                       const ConversionPreferences &preferences);
115 
116   // Sends a prediction request to the converter.
117   // The result is added at the tail of existing candidate list as "suggestion"
118   // candidates.
119   virtual bool ExpandSuggestion(const composer::Composer &composer);
120   virtual bool ExpandSuggestionWithPreferences(
121       const composer::Composer &composer,
122       const ConversionPreferences &preferences);
123 
124   // Clears conversion segments, but keep the context.
125   virtual void Cancel();
126 
127   // Clears conversion segments and the context.
128   virtual void Reset();
129 
130   // Fixes the conversion with the current status.
131   virtual void Commit(const composer::Composer &composer,
132                       const commands::Context &context);
133 
134   // Fixes the suggestion candidate. Stores the number of characters in the key
135   // of the committed candidate to committed_key_size.
136   // For example, assume that "日本語" was suggested as a candidate for "にほ".
137   // The key of the candidate is "にほんご". Therefore, when the candidate is
138   // committed by this function, consumed_key_size will be set to 4.
139   // |consumed_key_size| can be very large value
140   // (i.e. SessionConverter::kConsumedAllCharacters).
141   // In this case all the composition characters are consumed.
142   // Examples:
143   // - {composition: "にほんご|", value: "日本語", consumed_key_size: 4}
144   //   This is usual case of suggestion/composition.
145   // - {composition: "にほんg|", value: "日本語",
146   //    consumed_key_size: kConsumedAllCharacters}
147   //   kConsumedAllCharacters can be very large value.
148   // - {composition: "わた|しのなまえ", value: "綿",
149   //    consumed_key_size: 2}
150   //   (Non-Auto) Partial suggestion.
151   //   New composition is "しのなまえ".
152   // - {composition: "わたしのなまえ|", value: "私の",
153   //    consumed_key_size: 4}
154   //   Auto partial suggestion.
155   //   Consumed the composition partially and "なまえ" becomes composition.
156   // - {composition: "じゅえり", value: "クエリ",
157   //    consumed_key_size: 4}
158   //   Typing correction.
159   //   The value クエリ corresponds to raw composition "じゅえり".
160   // True is returned if the suggestion is successfully committed.
161   virtual bool CommitSuggestionByIndex(size_t index,
162                                        const composer::Composer &composer,
163                                        const commands::Context &context,
164                                        size_t *consumed_key_size);
165 
166   // Selects a candidate and commit the selected candidate.  True is
167   // returned if the suggestion is successfully committed.
168   // c.f. CommitSuggestionInternal
169   virtual bool CommitSuggestionById(int id,
170                                     const composer::Composer &composer,
171                                     const commands::Context &context,
172                                     size_t *consumed_key_size);
173 
174   // Fixes only the conversion of the first segment, and keep the rest.
175   // The caller should delete characters from composer based on returned
176   // |committed_key_size|.
177   // If |consumed_key_size| is 0, this means that there is only a segment
178   // so Commit() method is called instead. In this case, the caller
179   // should not delete any characters.
180   // c.f. CommitSuggestionInternal
181   virtual void CommitFirstSegment(const composer::Composer &composer,
182                                   const commands::Context &context,
183                                   size_t *consumed_key_size);
184 
185   // Does allmost the same thing as CommitFirstSegment.
186   // The only difference is to fix the segments from the head to the focused.
187   virtual void CommitHeadToFocusedSegments(const composer::Composer &composer,
188                                            const commands::Context &context,
189                                            size_t *consumed_key_size);
190 
191   // Commits the preedit string represented by Composer.
192   virtual void CommitPreedit(const composer::Composer &composer,
193                              const commands::Context &context);
194 
195   // Commits the specified number of characters at the head of the preedit
196   // string represented by Composer.
197   // The caller should delete characters from composer based on returned
198   // |consumed_key_size|.
199   // c.f. CommitSuggestionInternal
200   // TODO(yamaguchi): Enhance to support the conversion mode.
201   virtual void CommitHead(size_t count,
202                           const composer::Composer &composer,
203                           size_t *consumed_key_size);
204 
205   // Reverts the last "Commit" operation
206   virtual void Revert();
207 
208   // Moves the focus of segments.
209   virtual void SegmentFocusRight();
210   virtual void SegmentFocusLast();
211   virtual void SegmentFocusLeft();
212   virtual void SegmentFocusLeftEdge();
213 
214   // Resizes the focused segment.
215   virtual void SegmentWidthExpand(const composer::Composer &composer);
216   virtual void SegmentWidthShrink(const composer::Composer &composer);
217 
218   // Moves the focus of candidates.
219   virtual void CandidateNext(const composer::Composer &composer);
220   virtual void CandidateNextPage();
221   virtual void CandidatePrev();
222   virtual void CandidatePrevPage();
223   // Moves the focus to the candidate represented by the id.
224   virtual void CandidateMoveToId(int id, const composer::Composer &composer);
225   // Moves the focus to the index from the beginning of the current page.
226   virtual void CandidateMoveToPageIndex(size_t index);
227   // Moves the focus to the candidate represented by the shortcut.  If
228   // the shortcut is not bound with any candidate, false is returned.
229   virtual bool CandidateMoveToShortcut(char shortcut);
230 
231   // Operation for the candidate list.
232   virtual void SetCandidateListVisible(bool visible);
233 
234   // Fills protocol buffers and update the internal status.
235   virtual void PopOutput(const composer::Composer &composer,
236                          commands::Output *output);
237 
238   // Fills protocol buffers
239   virtual void FillOutput(const composer::Composer &composer,
240                           commands::Output *output) const;
241 
242   // Sets setting by the request;
243   virtual void SetRequest(const commands::Request *request);
244 
245   // Sets setting by the config;
246   virtual void SetConfig(const config::Config *config);
247 
248   // Set setting by the context.
249   virtual void OnStartComposition(const commands::Context &context);
250 
251   // Fills segments with the conversion preferences.
252   static void SetConversionPreferences(
253       const ConversionPreferences &preferences,
254       Segments *segments);
255 
256   // Copies SessionConverter
257   // TODO(hsumita): Copy all member variables.
258   // Currently, converter_ is not copied.
259   virtual SessionConverter *Clone() const;
260 
261   // Fills protocol buffers with all flatten candidate words.
262   void FillAllCandidateWords(commands::CandidateList *candidates) const;
263 
set_selection_shortcut(config::Config::SelectionShortcut selection_shortcut)264   virtual void set_selection_shortcut(
265       config::Config::SelectionShortcut selection_shortcut) {
266     selection_shortcut_ = selection_shortcut;
267   }
268 
set_use_cascading_window(bool use_cascading_window)269   virtual void set_use_cascading_window(bool use_cascading_window) {
270     use_cascading_window_ = use_cascading_window;
271   }
272 
273   // Meaning that all the composition characters are consumed.
274   // c.f. CommitSuggestionInternal
275   static const size_t kConsumedAllCharacters;
276 
277  private:
278   friend class SessionConverterTest;
279 
280   // Resets the result value stored at the previous command.
281   void ResetResult();
282 
283   // Resets the session state variables.
284   void ResetState();
285 
286   // Notifies the converter that the current segment is focused.
287   void SegmentFocus();
288 
289   // Notifies the converter that the current segment is fixed.
290   void SegmentFix();
291 
292   // Fixes the conversion of the [0, segments_to_commit -1 ] segments,
293   // and keep the rest.
294   // Internal implementation for CommitFirstSegment and
295   // CommitHeadToFocusedSegment.
296   void CommitSegmentsInternal(const composer::Composer &composer,
297                               const commands::Context &context,
298                               size_t segments_to_commit,
299                               size_t *consumed_key_size);
300 
301   // Gets preedit from segment(index) to segment(index + size).
302   void GetPreedit(size_t index, size_t size, string *preedit) const;
303   // Gets conversion from segment(index) to segment(index + size).
304   void GetConversion(size_t index, size_t size, string *conversion) const;
305   // Gets consumed size of the preedit characters.
306   // c.f. CommitSuggestionInternal
307   size_t GetConsumedPreeditSize(const size_t index, size_t size) const;
308 
309   // Performs the command if the command candidate is selected.  True
310   // is returned if a command is performed.
311   bool MaybePerformCommandCandidate(size_t index, size_t size);
312 
313   // Updates internal states
314   bool UpdateResult(size_t index, size_t size, size_t *consumed_key_size);
315 
316   // Fills the candidate list with the focused segment's candidates.
317   // This method does not clear the candidate list before processing.
318   // Only the candidates of which id is not existent in the candidate list
319   // are appended. Other candidates are ignored.
320   void AppendCandidateList();
321   // Clears the candidate list and fill it with the focused segment's
322   // candidates.
323   void UpdateCandidateList();
324 
325   // Returns the candidate index to be used by the converter.
326   int GetCandidateIndexForConverter(const size_t segment_index) const;
327 
328   // If focus_id is pointing to the last of suggestions,
329   // call StartPrediction().
330   void MaybeExpandPrediction(const composer::Composer &composer);
331 
332   // Returns the value of candidate to be used by the converter.
333   string GetSelectedCandidateValue(size_t segment_index) const;
334 
335   // Returns the candidate to be used by the converter.
336   const Segment::Candidate &GetSelectedCandidate(size_t segment_index) const;
337 
338   // Returns the length of committed candidate's key in characters.
339   // True is returned if the selected candidate is successfully committed.
340   bool CommitSuggestionInternal(const composer::Composer &composer,
341                                 const commands::Context &context,
342                                 size_t *committed_length);
343 
344   void SegmentFocusInternal(size_t segment_index);
345   void ResizeSegmentWidth(const composer::Composer &composer, int delta);
346 
347   void FillConversion(commands::Preedit *preedit) const;
348   void FillResult(commands::Result *result) const;
349   void FillCandidates(commands::Candidates *candidates) const;
350 
351   bool IsEmptySegment(const Segment &segment) const;
352 
353   // Handles selected_indices for usage stats.
354   void InitializeSelectedCandidateIndices();
355   void UpdateSelectedCandidateIndex();
356   void UpdateCandidateStats(const string &base_name, int32 index);
357   void CommitUsageStats(
358       SessionConverterInterface::State commit_state,
359       const commands::Context &context);
360   void CommitUsageStatsWithSegmentsSize(
361       SessionConverterInterface::State commit_state,
362       const commands::Context &context,
363       size_t submit_segment_size);
364 
365   SessionConverterInterface::State state_;
366 
367   const ConverterInterface *converter_;
368   std::unique_ptr<Segments> segments_;
369   size_t segment_index_;
370 
371   // Previous suggestions to be merged with the current predictions.
372   Segment previous_suggestions_;
373 
374   // Default conversion preferences.
375   ConversionPreferences conversion_preferences_;
376 
377   std::unique_ptr<commands::Result> result_;
378 
379   std::unique_ptr<CandidateList> candidate_list_;
380   bool candidate_list_visible_;
381 
382   const commands::Request *request_;
383   const config::Config *config_;
384 
385   // Mutable values of |config_|.  These values may be changed temporaliry per
386   // session.
387   bool use_cascading_window_;
388   config::Config::SelectionShortcut selection_shortcut_;
389 
390   // Indicates whether config_ will be updated by the command candidate.
391   Segment::Candidate::Command updated_command_;
392 
393   // Selected index data of each segments for usage stats.
394   std::vector<int> selected_candidate_indices_;
395 
396   // Revision number of client context with which the converter determines when
397   // the history segments should be invalidated. See the implemenation of
398   // OnStartComposition for details.
399   int32 client_revision_;
400 
401   DISALLOW_COPY_AND_ASSIGN(SessionConverter);
402 };
403 
404 }  // namespace session
405 }  // namespace mozc
406 
407 #endif  // MOZC_SESSION_SESSION_CONVERTER_H_
408