1  /*
     2   Copyright aswing.org, see the LICENCE.txt.
     3  */
     4    
     5  /**
     6   * To successfully store and retrieve (key->value) mapping from a HashMap.
     7   * HashMap accept any type of object to be the key: number, string, Object etc... 
     8   * But it is only get fast accessing with string type keys. Others are slow.
     9   * <p>
    10   * ----------------------------------------------------------
    11   * This example creates a HashMap of friends. It uses the number of the friends as keys:
    12   * <pre>
    13   *     function person(name,age,sex){
    14   *         this.name=name;
    15   *         this.age=age;
    16   *         this.sex=sex;
    17   *     }
    18   *     var friends = new HashMap();
    19   *     friends.put("one", new person("iiley",21,"M"));
    20   *     friends.put("two", new person("gothic man",22,"M"));
    21   *     friends.put("three", new person("rock girl",19,"F"));
    22   * </pre>
    23   * <p>To retrieve a friends, use the following code:
    24   *
    25   * <pre>
    26   *     var thisperson = friends.get("two");
    27   *     if (thisperson != null) {
    28   *         trace("two name is "+thisperson.name);
    29   *         trace("two age is "+thisperson.age);
    30   *         trace("two sex is "+thisperson.sex);
    31   *     }else{
    32   *         trace("two is not in friends!");
    33   *     }
    34   * </pre>
    35   *
    36   * @author: iiley
    37   */
    38  class org.aswing.utils.HashMap{
    39  
    40      private var length:Number;
    41      private var content:Object;
    42      private var keyContent:Object;
    43  	private var noneStringKeys:Array;
    44  	private var noneStringMapedValues:Array;
    45  	
    46  	
    47   	public function HashMap(){
    48          length = 0;
    49          content = new Object();
    50          keyContent = new Object();
    51          noneStringKeys = new Array();
    52          noneStringMapedValues = new Array();
    53   	}
    54  
    55   	//-------------------public methods--------------------
    56  
    57   	/**
    58    	 * Returns the number of keys in this HashMap.
    59    	 */
    60   	public function size():Number{
    61    		return length;
    62   	}
    63  
    64   	/**
    65    	 * Returns if this HashMap maps no keys to values.
    66    	 */
    67   	public function isEmpty():Boolean{
    68    		return (length==0);
    69   	}
    70  
    71   	/**
    72    	 * Returns an Array of the keys in this HashMap.
    73    	 */
    74   	public function keys():Array{
    75    		var temp:Array = new Array(length);
    76    		var index:Number = 0;
    77    		for(var i:String in keyContent){
    78     			temp[index] = keyContent[i];
    79     			index++;
    80    		}
    81    		for(var i:Number=0; i<noneStringKeys.length; i++){
    82    			temp[index+i] = noneStringKeys[i];
    83    		}
    84    		return temp;
    85   	}
    86   	
    87   	/**
    88    	 * Returns an Array of the values in this HashMap.
    89    	 */
    90   	public function values():Array{
    91    		var temp:Array = new Array(length);
    92    		var index:Number = 0;
    93    		for(var i:String in content){
    94     			temp[index] = content[i];
    95     			index++;
    96    		}
    97    		for(var i:Number=0; i<noneStringMapedValues.length; i++){
    98    			temp[index+i] = noneStringMapedValues[i];
    99    		}
   100    		return temp;
   101   	}
   102   	
   103   	private function isStringKey(key):Boolean{
   104   		return (typeof(key) == "string" || key instanceof String);
   105   	}
   106   	 	
   107   	private function indexOfKey(key):Number{
   108   		for(var i:Number=0; i<noneStringKeys.length; i++){
   109   			if(key === noneStringKeys[i]){
   110   				return i;
   111   			}
   112   		}
   113   		return -1;
   114   	}
   115   	
   116   	private function indexOfValue(value):Number{
   117   		for(var i:Number=0; i<noneStringMapedValues.length; i++){
   118   			if(value === noneStringMapedValues[i]){
   119   				return i;
   120   			}
   121   		}
   122   		return -1; 		
   123   	}
   124  
   125   	/**
   126    	 * Tests if some key maps into the specified value in this HashMap. 
   127    	 * This operation is more expensive than the containsKey method.
   128    	 */
   129   	public function containsValue(value):Boolean{
   130    		for(var i:String in content){
   131     			if(content[i] === value){
   132      			return true;
   133     			}
   134    		}
   135    		if(indexOfValue(value) >= 0){
   136    			return true;
   137    		}
   138   		return false;
   139   	}
   140  
   141   	/**
   142    	 * Tests if the specified object is a key in this HashMap.
   143    	 * This operation is very fast if it is a string.
   144       * @param   key   The key whose presence in this map is to be tested
   145       * @return <tt>true</tt> if this map contains a mapping for the specified
   146    	 */
   147   	public function containsKey(key):Boolean{
   148   		if(isStringKey(key)){
   149  	 		if(content[key] != undefined){
   150  	 			return true;
   151  	 		}
   152   		}else{
   153  	  		if(indexOfKey(key) >= 0){
   154  	  			return true;
   155  	  		}
   156   		}
   157    		return false;
   158   	}
   159  
   160   	/**
   161   	 * Returns the value to which the specified key is mapped in this HashMap.
   162   	 * Return null if the key is not mapped to any value in this HashMap.
   163    	 * This operation is very fast if the key is a string.
   164       * @param   key the key whose associated value is to be returned.
   165       * @return  the value to which this map maps the specified key, or
   166       *          <tt>null</tt> if the map contains no mapping for this key
   167       *           or it is null value originally.
   168   	 */
   169   	public function get(key){
   170   		if(isStringKey(key)){
   171  	 		var value = content[key];
   172  	 		if(value !== undefined){
   173  	 			return value;
   174  	 		}
   175   		}else{
   176  	 		var index:Number = indexOfKey(key);
   177  	 		if(index >= 0){
   178  	 			return noneStringMapedValues[index];
   179  	 		}
   180   		}
   181    		return null;
   182   	}
   183  
   184   	/**
   185   	 * Associates the specified value with the specified key in this map. 
   186   	 * If the map previously contained a mapping for this key, the old value is replaced. 
   187   	 * 
   188       * @param key key with which the specified value is to be associated.
   189       * @param value value to be associated with the specified key.
   190       * @return previous value associated with specified key, or <tt>null</tt>
   191       *	       if there was no mapping for key.  A <tt>null</tt> return can
   192       *	       also indicate that the HashMap previously associated
   193       *	       <tt>null</tt> with the specified key.
   194    	 */
   195   	public function put(key, value){
   196    		if(key == undefined){
   197     			trace("cannot put a value with undefined or null key!");
   198     			return undefined;
   199    		}else{
   200    			var exist:Boolean = containsKey(key);
   201   			if(!exist){
   202     				length++;
   203   			}
   204   			var oldValue = this.get(key);
   205   			if(isStringKey(key)){
   206  	   			content[key]=value;
   207  	   			keyContent[key]=key;
   208   			}else{
   209   				if(!exist){
   210  					noneStringKeys.push(key);
   211  					noneStringMapedValues.push(value);
   212   				}else{
   213   					var index:Number = indexOfKey(key);
   214   					noneStringKeys[index] = key;
   215   					noneStringMapedValues[index] = value;
   216   				}
   217   			}
   218     			return oldValue;
   219    		}
   220   	}
   221  
   222   	/**
   223       * Removes the mapping for this key from this map if present.
   224       *
   225       * @param  key key whose mapping is to be removed from the map.
   226       * @return previous value associated with specified key, or <tt>null</tt>
   227       *	       if there was no mapping for key.  A <tt>null</tt> return can
   228       *	       also indicate that the map previously associated <tt>null</tt>
   229       *	       with the specified key.
   230    	 */
   231   	public function remove(key){
   232   		var exist:Boolean = containsKey(key);
   233   		if(!exist){
   234   			return null;
   235   		}
   236    		var temp;
   237    		if(isStringKey(key)){
   238     			temp=content[key];
   239     			delete content[key];
   240     			delete keyContent[key];
   241    		}else{
   242   			var index:Number = indexOfKey(key);
   243   			temp = noneStringMapedValues[index];
   244   			noneStringKeys.splice(index, 1);
   245   			noneStringMapedValues.splice(index, 1);
   246    		}
   247     		length--;
   248    		return temp;
   249   	}
   250   	
   251   	/**
   252    	 * put a array's all element into the HashMap, 
   253    	 * key+index will be the key
   254    	 */
   255   	public function putArray(Arr:Array, key:String):Void{
   256    		for(var i:Number=0; i<Arr.length; i++){
   257     			put(key+i, Arr[i]);
   258    		}
   259   	}
   260  
   261   	/**
   262   	 * Clears this HashMap so that it contains no keys no values.
   263   	 */
   264   	public function clear():Void{
   265    		length = 0;
   266    		content = new Object();
   267    		keyContent = new Object();
   268          noneStringKeys = new Array();
   269          noneStringMapedValues = new Array();
   270   	}
   271  
   272   	/**
   273   	 * Return a same copy of HashMap object
   274   	 */
   275   	public function clone():HashMap{
   276    		var temp:HashMap = new HashMap();
   277    		for(var i:String in content){
   278     			temp.put(keyContent[i], content[i]);
   279    		}
   280    		for(var i:Number=0; i<noneStringKeys.length; i++){
   281    			temp.put(noneStringKeys[i], noneStringMapedValues[i]);
   282    		}
   283    		return temp;
   284   	}
   285  
   286   	public function toString():String{
   287    		var ks:Array = keys();
   288    		var vs:Array = values();
   289    		var temp:String = "HashMap Content:\n";
   290    		for(var i:Number=0; i<ks.length; i++){
   291     			temp += ks[i]+" -> "+vs[i] + "\n";
   292    		}
   293    		return temp;
   294   	}	
   295  }
   296