1 
2 /******************************************************
3  *  Presage, an extensible predictive text entry system
4  *  ---------------------------------------------------
5  *
6  *  Copyright (C) 2008  Matteo Vescovi <matteo.vescovi@yahoo.co.uk>
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21                                                                              *
22                                                                 **********(*)*/
23 
24 
25 #include "dejavuPredictor.h"
26 
27 #include <algorithm>
28 
29 /*
30  * Implementation idea: predictor remembers previously entered text (by
31  * storing it in a config defined file); when current token is found
32  * in the remembered text, the tokens following the current token are
33  * suggested; this requires "learning" previously entered text.
34  *
35  */
36 
DejavuPredictor(Configuration * config,ContextTracker * ct,const char * name)37 DejavuPredictor::DejavuPredictor(Configuration* config, ContextTracker* ct, const char* name)
38     : Predictor(config,
39                 ct,
40                 name,
41                 "DejavuPredictor, a parrot predictor",
42                 "DejavuPredictor is a parrot predictor.\n"
43                 "It always returns what it has heard before.\n"),
44       dispatcher (this)
45 {
46     LOGGER = PREDICTORS + name + ".LOGGER";
47     MEMORY = PREDICTORS + name + ".MEMORY";
48     TRIGGER = PREDICTORS + name + ".TRIGGER";
49 
50     // build notification dispatch map
51     dispatcher.map (config->find (LOGGER), & DejavuPredictor::set_logger);
52     dispatcher.map (config->find (MEMORY), & DejavuPredictor::set_memory);
53     dispatcher.map (config->find (TRIGGER), & DejavuPredictor::set_trigger);
54 }
55 
~DejavuPredictor()56 DejavuPredictor::~DejavuPredictor()
57 {}
58 
set_memory(const std::string & filename)59 void DejavuPredictor::set_memory (const std::string& filename)
60 {
61     memory = filename;
62     logger << INFO << "MEMORY: " << filename << endl;
63 }
64 
set_trigger(const std::string & number)65 void DejavuPredictor::set_trigger (const std::string& number)
66 {
67     trigger = Utility::toInt (number);
68     logger << INFO << "TRIGGER: " << number << endl;
69 }
70 
predict(const size_t max_partial_predictions_size,const char ** filter) const71 Prediction DejavuPredictor::predict(const size_t max_partial_predictions_size, const char** filter) const
72 {
73     Prediction result;
74 
75     std::list<std::string> memory_trigger;
76 
77     std::ifstream memory_file(memory.c_str());
78     if (!memory_file) {
79         logger << ERROR << "Error opening memory file: " << memory << endl;
80     } else {
81         if (init_memory_trigger(memory_trigger)) {
82             logger << INFO << "Memory trigger init'ed" << endl;
83 
84             std::list<std::string> rolling_window;
85             if (init_rolling_window(rolling_window, memory_file)) {
86                 logger << INFO << "Rolling window init'ed" << endl;
87 
88                 std::string token;
89                 while (memory_file >> token) {
90                     logger << INFO << "Analyzing token: " << token << endl;
91 
92                     if (match(memory_trigger, rolling_window)) {
93                         logger << INFO << "Token triggered memory: " << token << endl;
94                         if (token_satisfies_filter (token, contextTracker->getPrefix(), filter)) {
95                             logger << INFO << "Adding suggestion: " << token << endl;
96                             result.addSuggestion(Suggestion(token, 1.0));
97                         }
98                     }
99 
100                     update_rolling_window(rolling_window, token);
101                 }
102 
103             } else {
104                 logger << INFO << "Rolling window initialization failed." << endl;
105             }
106         } else {
107             logger << INFO << "Memory trigger initialization failed." << endl;
108         }
109 
110         memory_file.close();
111     }
112 
113     return result;
114 }
115 
learn(const std::vector<std::string> & change)116 void DejavuPredictor::learn(const std::vector<std::string>& change)
117 {
118     // loop through all tokens in change vector
119     for (std::vector<std::string>::const_iterator it = change.begin();
120          it != change.end();
121          ++it)
122     {
123         std::string new_token = *it;
124         logger << INFO << "Committing new token to memory: " << new_token << endl;
125         std::ofstream memory_file(memory.c_str(), std::ios::app);
126         if (!memory_file) {
127             logger << ERROR << "Error opening memory file: " << memory << endl;
128         } else {
129             memory_file << new_token << std::endl;
130             memory_file.close();
131         }
132     }
133 }
134 
135 /** Tests two list arguments match.
136  *
137  * @return true if lists contain the same tokens in the same order,
138  * false otherwise
139  */
match(const std::list<std::string> & l1,const std::list<std::string> & l2) const140 bool DejavuPredictor::match(const std::list<std::string>& l1, const std::list<std::string>& l2) const
141 {
142     return equal(l1.begin(), l1.end(), l2.begin());
143 }
144 
145 /** Initialize memory trigger.
146  *
147  * @param memory_trigger contains tokens that will trigger a memory
148  * recollection
149  *
150  * @return true if memory trigger has been populated with enough
151  * tokens to trigger a memory recollection, false otherwise
152  *
153  */
init_memory_trigger(std::list<std::string> & memory_trigger) const154 bool DejavuPredictor::init_memory_trigger(std::list<std::string>& memory_trigger) const
155 {
156     bool result = false;
157 
158     // fill up the token window list that contains the tokens that
159     // will trigger a recollection
160     for (int i = trigger; i > 0; i--) {
161         logger << INFO << "Memory trigger list: " << contextTracker->getToken(i) << endl;
162         memory_trigger.push_back(contextTracker->getToken(i));
163     }
164 
165     // check that the current context is rich enough to trigger a
166     // recollection
167     if (memory_trigger.end() == find(memory_trigger.begin(), memory_trigger.end(), "")) {
168         result = true;
169     }
170 
171     logger << INFO << "Memory trigger valid: " << result << endl;
172 
173     return result;
174 }
175 
176 /** Initialize rolling window.
177  *
178  * @return true if initialized rolling window has been populated with
179  * enough tokens to trigger a memory recollection, false otherwise
180  *
181  */
init_rolling_window(std::list<std::string> & rolling_window,std::ifstream & memory_file) const182 bool DejavuPredictor::init_rolling_window(std::list<std::string>& rolling_window, std::ifstream& memory_file) const
183 {
184     std::string token;
185     int count = 0;
186     // following while test relies on the fact that if first condition
187     // is true, then the second condition will not be evaluated: in
188     // other words, a token will not be read from file if count is not
189     // less than trigger.
190     while (count < trigger && memory_file >> token) {
191         logger << INFO << "Rolling window list: " << token << endl;
192         rolling_window.push_back(token);
193         count++;
194     }
195 
196     return (count == trigger);
197 }
198 
199 /** Update rolling window.
200  *
201  * Pop front token and push back new token to list.
202  */
update_rolling_window(std::list<std::string> & rolling_window,const std::string & token) const203 void DejavuPredictor::update_rolling_window(std::list<std::string>& rolling_window, const std::string& token) const
204 {
205     rolling_window.pop_front();
206     logger << INFO << "Pushing back on memory list: " << token << endl;
207     rolling_window.push_back(token);
208 }
209 
update(const Observable * var)210 void DejavuPredictor::update (const Observable* var)
211 {
212     logger << DEBUG << "About to invoke dispatcher: " << var->get_name () << " - " << var->get_value() << endl;
213     dispatcher.dispatch (var);
214 }
215