Codebase list mozc / d2e045e chrome / skk / history.js
d2e045e

Tree @d2e045e (Download .tar.gz)

history.js @d2e045eraw · history · blame

// Copyright 2010-2012, 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.

/**
 * @fileoverview This file contains skk.History class implementation.
 */

'use strict';

console.assert(skk, 'ime.js must be loaded prior to this module');

/**
 * ASCII record separator character.
 * @const
 */
skk.RECORD_SEPARATOR = String.fromCharCode('0x1E');

/**
 * Max number of history entries per word.
 * @const
 */
skk.MAX_HISTORY_PER_WORD = 8;

/**
 * Max number of kinds of words.
 * @const
 */
skk.MAX_WORDS = 5000;

/**
 * skk.History records recently selected conversion candidates.
 * @constructor
 */
skk.History = function() {
  /**
   * Dictionary holding recently used conversion candidates per word.
   * @private
   * @type Object
   */
  this.histories_ = {};
  /**
   * Holds registered words in LRU order. When the size of the array exceeds
   * {@code skk.MAX_WORDS}, History having the key equivalent to the head of
   * this array is removed.
   * @private
   * @type Array.<string>
   */
  this.registeredKeys_ = [];
};

/**
 * @param {string} base No conjugation part of word. 1+ length Hiragana string.
 * @param {string} stem Stem of conjugation. 0+ length latin string.
 * @return {string} Key string for lookup.
 * @private
 */
skk.History.prototype.getKeyFor_ = function(base, stem) {
  return base + skk.RECORD_SEPARATOR + stem;
};

/**
 * Serializes conversion history as a JSON string.
 * @return {string} JSON string which is able to be restored as object status by
 *     {@code deserialize}.
 * @see skk.History#deserialize
 */
skk.History.prototype.serialize = function() {
  return JSON.stringify({ histories: this.histories_,
                          registeredWords: this.registeredWords_ });
};

/**
 * Restores object status from serialized string.
 * @param {string} serialized JSON string returned from {@code deserialize}.
 * @see skk.History#serialize
 */
skk.History.prototype.deserialize = function(serialized) {
  var parsed = JSON.parse(serialized);
  this.histories_ = parsed.histories;
  this.registeredWords_ = parsed.registeredWords;
};

/**
 * Looks up recently used conversion candidates.
 * @param {string} base No conjugation part of word. 1+ length Hiragana string.
 * @param {string} stem Stem of conjugation. 0+ length latin string.
 * @return {Array.<string>} Conversion candidates. Ordered in LIFO.
 * @see skk.History#addCandidate
 */
skk.History.prototype.lookup = function(base, stem) {
  var candidates = this.histories_[this.getKeyFor_(base, stem)];
  if (!candidates) { return []; }
  return candidates.slice(0);  // Returns a copy
};

/**
 * Adds a candidate. If the candidate is already in the history, it is moved
 * to the top of candidate list.
 * @param {string} base No conjugation part of word. 1+ length Hiragana string.
 * @param {string} stem Stem of conjugation. 0+ length latin string.
 * @param {string} converted Converted string to be added to history.
 */
skk.History.prototype.addCandidate = function(base, stem, converted) {
  var key = this.getKeyFor_(base, stem);
  var candidates = this.histories_[key];
  if (!candidates) {
    candidates = [];
    this.histories_[key] = candidates;
    this.registeredKeys_.push(key);
  } else {
    for (var i = 0; i < this.registeredKeys_.length; ++i) {
      if (this.registeredKeys_[i] === key) {
        this.registeredKeys_.splice(i, 1);
        this.registeredKeys_.push(key);
        break;
      }
    }
  }
  for (var i = 0; i < candidates.length; ++i) {
    if (candidates[i] == converted) {
      candidates.splice(i, 1);
      break;
    }
  }
  candidates.unshift(converted);
  while (candidates.length > skk.MAX_HISTORY_PER_WORD) { candidates.pop(); }
  while (this.registeredKeys_.length > skk.MAX_WORDS) {
    var oldKey = this.registeredKeys_.shift();
    delete this.histories_[oldKey];
  }
};