presage  0.8.7
selector.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************
00003  *  Presage, an extensible predictive text entry system
00004  *  ---------------------------------------------------
00005  *
00006  *  Copyright (C) 2008  Matteo Vescovi <matteo.vescovi@yahoo.co.uk>
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License along
00019     with this program; if not, write to the Free Software Foundation, Inc.,
00020     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00021                                                                              *
00022                                                                 **********(*)*/
00023 
00024 
00025 #include "selector.h"
00026 #include "utility.h"
00027 
00028 const char* Selector::SUGGESTIONS = "Presage.Selector.SUGGESTIONS";
00029 const char* Selector::REPEAT_SUGGESTIONS = "Presage.Selector.REPEAT_SUGGESTIONS";
00030 const char* Selector::GREEDY_SUGGESTION_THRESHOLD = "Presage.Selector.GREEDY_SUGGESTION_THRESHOLD";
00031 
00032 const char* Selector::LOGGER = "Presage.Selector.LOGGER";
00033 
00034 Selector::Selector(Configuration* configuration, ContextTracker* ct)
00035     : contextTracker(ct),
00036       config(configuration),
00037       logger("Selector", std::cerr),
00038       dispatcher(this)
00039 {
00040     // build notification dispatch map
00041     dispatcher.map (config->find (LOGGER), & Selector::set_logger);
00042     dispatcher.map (config->find (SUGGESTIONS), & Selector::set_suggestions);
00043     dispatcher.map (config->find (REPEAT_SUGGESTIONS), & Selector::set_repeat_suggestions);
00044     dispatcher.map (config->find (GREEDY_SUGGESTION_THRESHOLD), & Selector::set_greedy_suggestion_threshold);
00045 
00046     // set prefix
00047     previous_prefix = contextTracker->getPrefix();
00048 }
00049 
00050 Selector::~Selector()
00051 {
00052     // nothing to do here, move along
00053 }
00054 
00055 std::vector<std::string> Selector::select( Prediction p )
00056 {
00057     // copy words from Prediction.Suggestion.word in result vector
00058     std::vector<std::string> result;
00059     std::string token;
00060     for (size_t i=0 ; i<p.size() ; i++) {
00061         token =  p.getSuggestion(i).getWord();
00062         result.push_back(token);
00063         logger << DEBUG << "Added token to selector consideration set: " << token << endl;
00064     }
00065         
00066     // check whether user has not moved on to a new word
00067     if (contextTracker->contextChange()) {
00068         logger << DEBUG << "Context change detected." << endl;
00069         clearSuggestedWords();
00070     } else {
00071         logger << DEBUG << "No context change detected." << endl;
00072     }
00073 
00074     // filter out suggestions that do not satisfy repetition constraint
00075     if( !repeat_suggestions )
00076         repetitionFilter( result );
00077 
00078     // filter out suggestions that do not satisfy threshold constraint
00079     if( greedy_suggestion_threshold > 0 )
00080         thresholdFilter( result );
00081 
00082     // build result
00083         
00084     // check that we have enough selected words
00085     if( result.size() < static_cast<unsigned int>(suggestions) ) {
00086         // Job's not done, got to get a bigger Prediction
00087         // we should invoke predict() to get more Suggestions
00088 
00089         // could throw an exception that would be caught by predictor
00090         // which would reissue the predict call to get more
00091         // suggestions
00092                         
00093         // TODO <============================================
00094                         
00095         // just abort for now
00096         //std::cerr << "Not enough Suggestions" << std::endl;
00097         //abort();
00098                 
00099     } else {
00100         // erase the requested number of words
00101         result.erase( result.begin() + suggestions, result.end() );
00102     }
00103 
00104     // update suggested words set
00105     updateSuggestedWords( result );
00106 
00107     return result;
00108 }
00109 
00110 
00114 void Selector::update()
00115 {
00116     // check whether user has not moved on to a new word
00117     if (contextTracker->contextChange()) {
00118         clearSuggestedWords();
00119     }
00120 }
00121 
00122 
00126 void Selector::updateSuggestedWords( const std::vector<std::string>& v )
00127 {
00128     std::vector<std::string>::const_iterator i = v.begin();
00129     while( i != v.end() ) {
00130         logger << DEBUG << "Adding token to suggested token set: " << *i << endl; 
00131         suggestedWords.insert( *i );
00132         i++;
00133     }
00134 
00135     logger << DEBUG << "Suggested words: ";
00136     for (StringSet::const_iterator it = suggestedWords.begin();
00137          it != suggestedWords.end();
00138          it++) {
00139         logger << *it << ' ';
00140     }
00141     logger << endl;
00142 }
00143 
00144 
00148 void Selector::clearSuggestedWords()
00149 {
00150     logger << DEBUG << "Clearing previously suggested tokens set." << endl;
00151     suggestedWords.clear();
00152 }
00153 
00162 void Selector::repetitionFilter( std::vector<std::string>& v )
00163 {
00164     std::vector< std::string > temp;
00165 
00166     for( std::vector<std::string>::iterator i = v.begin();
00167          i != v.end();
00168          i++ ) {
00169         if( suggestedWords.find( *i ) == suggestedWords.end() ) {
00170             temp.push_back( *i );
00171             logger << DEBUG << "Token passed repetition filter: " << *i << endl;
00172         } else {
00173             logger << DEBUG << "Token failed repetition filter: " << *i << endl;
00174         }
00175     }
00176 
00177     v = temp;
00178 }
00179 
00187 void Selector::thresholdFilter( std::vector<std::string>& v )
00188 {
00189     assert( greedy_suggestion_threshold >= 0 );
00190 
00191     // zero threshold indicates feature is disabled
00192     if( greedy_suggestion_threshold != 0 ) {
00193                 
00194         int length = contextTracker->getPrefix().size();
00195         std::vector<std::string>::iterator i = v.begin();
00196         while (i != v.end()) {
00197             if( (i->size()-length) < greedy_suggestion_threshold) {
00198                 logger << INFO << "Removing token: " << *i << endl;
00199                 i = v.erase( i );
00200             } else {
00201                 i++;
00202             }
00203         }
00204     }
00205 }
00206 
00207 
00211 void Selector::set_logger (const std::string& value)
00212 {
00213     logger << setlevel (value);
00214     logger << INFO << "LOGGER: " << value << endl;
00215 }
00216 
00217 
00221 void Selector::set_suggestions(const std::string& value)
00222 {
00223     logger << INFO << "SUGGESTIONS: " << value << endl;
00224     int result = Utility::toInt(value);
00225     if (result < 0) {
00226         logger << ERROR << "Presage.Selector.SUGGESTIONS value out of range!/a" << endl;
00227         // REVISIT: throw exception
00228         abort();
00229     }
00230 
00231     suggestions = result;
00232 }
00233 
00234 
00238 void Selector::set_repeat_suggestions(const std::string& value)
00239 {
00240     logger << INFO << "REPEAT_SUGGESTIONS: " << value << endl;
00241     bool result = Utility::isYes(value);
00242 
00243     repeat_suggestions = result;
00244 }
00245 
00246 
00250 void Selector::set_greedy_suggestion_threshold(const std::string& value)
00251 {
00252     logger << INFO << "GREEDY_SUGGESTION_THRESHOLD: " << value << endl;
00253     int result = Utility::toInt(value);
00254     if( result < 0 ) {
00255         logger << ERROR << "GREEDY_SUGGESTION_THRESHOLD value out of range." << value << endl;
00256         // REVISIT: throw exception
00257         abort();
00258     }
00259 
00260     greedy_suggestion_threshold = result;
00261 }
00262 
00263 size_t Selector::get_suggestions () const
00264 {
00265     return suggestions;
00266 }
00267 
00268 bool Selector::get_repeat_suggestions () const
00269 {
00270     return repeat_suggestions;
00271 }
00272 
00273 size_t Selector::get_greedy_suggestion_threshold () const
00274 {
00275     return greedy_suggestion_threshold;
00276 }
00277 
00278 void Selector::update (const Observable* variable)
00279 {
00280     logger << DEBUG << "update(" << variable->get_name () << ") called" << endl;
00281 
00282     dispatcher.dispatch (variable);
00283 }