1 /*
  2  * ElementIterator.js - Iterate through a list of collation elements
  3  *
  4  * Copyright © 2013-2015, JEDLSoft
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  *
 10  *     http://www.apache.org/licenses/LICENSE-2.0
 11  *
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 /**
 21  * @class
 22  * An iterator through a sequence of collation elements. This
 23  * iterator takes a source of code points, converts them into
 24  * collation elements, and allows the caller to get single
 25  * elements at a time.
 26  *
 27  * @constructor
 28  * @private
 29  * @param {CodePointSource} source source of code points to
 30  * convert to collation elements
 31  * @param {Object} map mapping from sequences of code points to
 32  * collation elements
 33  * @param {number} keysize size in bits of the collation elements
 34  */
 35 var ElementIterator = function (source, map, keysize) {
 36     this.elements = [];
 37     this.source = source;
 38     this.map = map;
 39     this.keysize = keysize;
 40 };
 41 
 42 /**
 43  * @private
 44  */
 45 ElementIterator.prototype._fillBuffer = function () {
 46     var str = undefined;
 47 
 48     // peek ahead by up to 4 characters, which may combine
 49     // into 1 or more collation elements
 50     for (var i = 4; i > 0; i--) {
 51         str = this.source.peek(i);
 52         if (str && this.map[str]) {
 53             this.elements = this.elements.concat(this.map[str]);
 54             this.source.consume(i);
 55             return;
 56         }
 57     }
 58 
 59     if (str) {
 60         // no mappings for the first code point, so just use its
 61         // Unicode code point as a proxy for its sort order. Shift
 62         // it by the key size so that everything unknown sorts
 63         // after things that have mappings
 64         this.elements.push(str.charCodeAt(0) << this.keysize);
 65         this.source.consume(1);
 66     } else {
 67         // end of the string
 68         return undefined;
 69     }
 70 };
 71 
 72 /**
 73  * Return true if there are more collation elements left to
 74  * iterate through.
 75  * @returns {boolean} true if there are more elements left to
 76  * iterate through, and false otherwise
 77  */
 78 ElementIterator.prototype.hasNext = function () {
 79     if (this.elements.length < 1) {
 80         this._fillBuffer();
 81     }
 82     return !!this.elements.length;
 83 };
 84 
 85 /**
 86  * Return the next collation element. If more than one collation
 87  * element is generated from a sequence of code points
 88  * (ie. an "expansion"), then this class will buffer the
 89  * other elements and return them on subsequent calls to
 90  * this method.
 91  *
 92  * @returns {number|undefined} the next collation element or
 93  * undefined for no more collation elements
 94  */
 95 ElementIterator.prototype.next = function () {
 96     if (this.elements.length < 1) {
 97         this._fillBuffer();
 98     }
 99     var ret = this.elements[0];
100     this.elements = this.elements.slice(1);
101     return ret;
102 };
103 
104 module.exports = ElementIterator;
105