1 /*
  2  * 	Copyright (C) 2012-2013 DFKI GmbH
  3  * 	Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
  4  * 	German Research Center for Artificial Intelligence
  5  * 	http://www.dfki.de
  6  * 
  7  * 	Permission is hereby granted, free of charge, to any person obtaining a 
  8  * 	copy of this software and associated documentation files (the 
  9  * 	"Software"), to deal in the Software without restriction, including 
 10  * 	without limitation the rights to use, copy, modify, merge, publish, 
 11  * 	distribute, sublicense, and/or sell copies of the Software, and to 
 12  * 	permit persons to whom the Software is furnished to do so, subject to 
 13  * 	the following conditions:
 14  * 
 15  * 	The above copyright notice and this permission notice shall be included 
 16  * 	in all copies or substantial portions of the Software.
 17  * 
 18  * 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 19  * 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 20  * 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 21  * 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 22  * 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 23  * 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 24  * 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 25  */
 26 
 27 define(
 28 	//this comment is needed by jsdoc2 [copy of comment for: function Dictionary(...]
 29 	/**
 30 	 * A dictionary (or map) for key-value storage and access.
 31 	 * @name Dictionary
 32 	 * @class
 33 	 */
 34 	function(){
 35 
 36 //set to @ignore in order to avoid doc-duplication in jsdoc3
 37 /**
 38  * @ignore
 39  * 
 40  * A dictionary (or map) for key-value storage and access.
 41  * @constructs Dictionary
 42  */
 43 function Dictionary() {
 44 
 45 	/**
 46 	 * "map" for the dictionary
 47 	 * 
 48 	 * @private
 49 	 * 
 50 	 * @memberOf Dictionary#
 51 	 */
 52 	var map = {};
 53 
 54 	/**
 55 	 * This list contains the "keys" of all current entries in <tt>map</tt>.
 56 	 * 
 57 	 * @private
 58 	 * 
 59 	 * @memberOf Dictionary#
 60 	 */
 61 	var keyList = [];
 62 
 63 	/**
 64 	 * Prefix for keys in internal MAP object, for avoiding overwrite of
 65 	 * existing Object properties/functions
 66 	 * 
 67 	 * @constant
 68 	 * @private
 69 	 * 
 70 	 * @memberOf Dictionary#
 71 	 */
 72 	var KEY_PREFIX = '$$';
 73 
 74 	/**
 75 	 * Helper function that creates the actual lookup key.
 76 	 * 
 77 	 * The "lookup key" is original key with applied key-prefix.
 78 	 * 
 79 	 * @param {String} key
 80 	 * 			the key (without the internal key prefix)
 81 	 * @returns {String}
 82 	 * 			the key prefixed with the internal key prefix
 83 	 * 
 84 	 * @private
 85 	 * 
 86 	 * @memberOf Dictionary#
 87 	 */
 88 	var lookupKey = function(key) {
 89 		return KEY_PREFIX + key;
 90 	};
 91 
 92 	/** @lends Dictionary.prototype */
 93 	return {
 94 		/**
 95 		 * Put / add an entry to the dictionary.
 96 		 * 
 97 		 * @param {String}
 98 		 *            key the lookup key for the value
 99 		 * @param {any}
100 		 *            value the value to store
101 		 * 
102 		 * @public
103 		 * 
104 		 * @memberOf Dictionary.prototype
105 		 */
106 		put : function(key, value) {
107 
108 			var isAlreadyPresent = this.containsKey(key);
109 
110 			var lKey = lookupKey(key);
111 			map[lKey] = value;
112 
113 			if (!isAlreadyPresent) {
114 				keyList.push(lKey);
115 			}
116 		},
117 		/**
118 		 * Check if the dictionary contains an entry for a key.
119 		 * 
120 		 * @param {String}
121 		 *            key the lookup key to check
122 		 * @returns {Boolean} <code>true</code> if an entry exists, otherwise
123 		 *          <code>false</code>
124 		 * 
125 		 * @public
126 		 */
127 		containsKey : function(key) {
128 			return typeof map[lookupKey(key)] !== 'undefined';
129 		},
130 		/**
131 		 * Check if the dictionary contains an entry with the value.
132 		 * 
133 		 * <p>
134 		 * NOTE that this function may execute rather slowly, with O(n).
135 		 * 
136 		 * @param {any}
137 		 *            value the value to check
138 		 * @param {Boolean}
139 		 *            [useStrict] if <code>true</code> entry-values are
140 		 *            checked against param <tt>value</tt> with
141 		 *            <code>===</code>. If <code>false</code> or omitted,
142 		 *            values are compared with each other using <code>==</code>.
143 		 * @returns {Boolean} <code>true</code> if an entry exists, otherwise
144 		 *          <code>false</code>
145 		 * 
146 		 * @public
147 		 */
148 		containsValue : function(value, useStrict) {
149 			for (var i = 0, size = keyList.length; i < size; ++i) {
150 				if (useStrict) {
151 					if (map[keyList[i]] === value) {
152 						return true;
153 					}
154 				} else {
155 					if (map[keyList[i]] == value) {
156 						return true;
157 					}
158 				}
159 			}
160 			return false;
161 		},
162 		/**
163 		 * Get the value for a key.
164 		 * 
165 		 * @param {String}
166 		 *            key the lookup key with was used to store the entry/value.
167 		 * @returns {any} the value for the <tt>key</tt>, or
168 		 *          <code>undefined</code> if the dictionary has no entry for
169 		 *          the <tt>key</tt>.
170 		 * 
171 		 * @public
172 		 */
173 		get : function(key) {
174 			return map[lookupKey(key)];
175 		},
176 		/**
177 		 * Remove an entry from the dictionary.
178 		 * 
179 		 * <p>
180 		 * NOTE that this may execute rather slowly, with O(n).
181 		 * 
182 		 * 
183 		 * @param {String}
184 		 *            key the lookup key for the entry to remove
185 		 * @returns {Boolean} <code>true</code> if the entry was removed. If
186 		 *          there was no entry for the <tt>key</tt> and nothing was
187 		 *          removed, <code>false</code> is returned.
188 		 * 
189 		 * @public
190 		 */
191 		remove : function(key) {
192 
193 			if (this.containsKey(key)) {
194 
195 				var lKey = lookupKey(key);
196 
197 				// remove from map:
198 				delete map[lKey];
199 
200 				// remove from key-list
201 				for (var i = 0, size = keyList.length; i < size; ++i) {
202 					if (keyList[i] == lKey) {
203 						keyList.splice(i, 1);
204 						break;
205 					}
206 				}
207 				return true;
208 			}
209 
210 			return false;
211 		},
212 		/**
213 		 * Get a list of the keys for all entries in the dictionary.
214 		 * 
215 		 * <p>
216 		 * The returned list has no specific ordering.
217 		 * 
218 		 * <p>
219 		 * NOTE that this may execute rather slowly, with O(n).
220 		 * 
221 		 * <p>
222 		 * NOTE that the returned list is no "view" for the keys, i.e. changes
223 		 * on this list will not be reflected by the dictionary's key-list.
224 		 * 
225 		 * @returns {Array<String>} a list of all keys
226 		 * @public
227 		 */
228 		getKeys : function() {
229 			var prefixLen = KEY_PREFIX.length;
230 			var size = keyList.length;
231 			var list = new Array(size);
232 			// create copy of keyList with removed key-prefixes:
233 			for (var i = 0; i < size; ++i) {
234 				list[i] = keyList[i].substring(prefixLen);
235 			}
236 			return list;
237 		},
238 		/**
239 		 * Get the size of the dictionary.
240 		 * 
241 		 * @returns {Number} the count of entries in the dictionary
242 		 * @public
243 		 */
244 		size : function() {
245 			return keyList.length;
246 		},
247 		/**
248 		 * Remove all entries from the dictionary.
249 		 * 
250 		 * <p>
251 		 * NOTE that this may execute rather slowly, with O(n).
252 		 * 
253 		 * @public
254 		 */
255 		clear : function() {
256 			// var size = keyList.length;
257 			// for(var i=0; i < size; ++i){
258 			// delete map[keyList[i]];
259 			// }
260 			// keyList.splice(0, size);
261 			delete map;
262 			map = {};
263 			keyList.splice(0, keyList.length);
264 		}
265 	};
266 };
267 
268 return Dictionary;
269 });
270