content: /*******************************************************************************
   * Copyright (c) 2009-2011, 2013 Luaj.org. All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   * 
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   ******************************************************************************/
  package org.luaj.vm2;
  
  import java.lang.ref.WeakReference;
  
  import org.luaj.vm2.LuaTable.Slot;
  import org.luaj.vm2.LuaTable.StrongSlot;
  
  /**
   * Subclass of {@link LuaTable} that provides weak key and weak value semantics. 
   * <p> 
   * Normally these are not created directly, but indirectly when changing the mode 
   * of a {@link LuaTable} as lua script executes.  
   * <p>
   * However, calling the constructors directly when weak tables are required from 
   * Java will reduce overhead.  
   */
  public class WeakTable implements Metatable {
  
  	private boolean weakkeys, weakvalues;
  	private LuaValue backing;
  
  	public static LuaTable make(boolean weakkeys, boolean weakvalues) {
  		LuaString mode;
  		if ( weakkeys && weakvalues ) {
  			mode = LuaString.valueOf("kv");
  		} else if ( weakkeys ) {
  			mode = LuaString.valueOf("k");
  		} else if ( weakvalues ) {
  			mode = LuaString.valueOf("v");
  		} else {
  			return LuaTable.tableOf();
  		}
  		LuaTable table = LuaTable.tableOf();
  		LuaTable mt = LuaTable.tableOf(new LuaValue[] { LuaValue.MODE, mode });
  		table.setmetatable(mt);
  		return table;
  	}
  
  	/**
  	 * Construct a table with weak keys, weak values, or both
  	 * @param weakkeys true to let the table have weak keys
  	 * @param weakvalues true to let the table have weak values
  	 */
  	public WeakTable(boolean weakkeys, boolean weakvalues, LuaValue backing) {
  		this.weakkeys = weakkeys;
  		this.weakvalues = weakvalues;
  		this.backing = backing;
  	}
  
  	public boolean useWeakKeys() {
  		return weakkeys;
  	}
  
  	public boolean useWeakValues() {
  		return weakvalues;
  	}
  
  	public LuaValue toLuaValue() {
  		return backing;
  	}
  
  	public Slot entry(LuaValue key, LuaValue value) {
  		value = value.strongvalue();
  		if ( value == null )
  			return null;
  		if ( weakkeys && !( key.isnumber() || key.isstring() || key.isboolean() )) {
  			if ( weakvalues && !( value.isnumber() || value.isstring() || value.isboolean() )) {
  				return new WeakKeyAndValueSlot( key, value, null );
  			} else {
  				return new WeakKeySlot( key, value, null );
  			}
  		}
  		if ( weakvalues && ! (value.isnumber() || value.isstring() || value.isboolean() )) {
  			return new WeakValueSlot( key, value, null );
  		}
  		return LuaTable.defaultEntry( key, value );
  	}
  
  	public static abstract class WeakSlot implements Slot {
  
  		protected Object key;
  		protected Object value;
  		protected Slot next;
  
  		protected WeakSlot(Object key, Object value, Slot next) {
  			this.key = key;
  			this.value = value;
  			this.next = next;
  		}
  
  		public abstract int keyindex( int hashMask );
  
  		public abstract Slot set(LuaValue value);
  
  		public StrongSlot first() {
  			LuaValue key = strongkey();
  			LuaValue value = strongvalue();
  			if ( key != null && value != null ) {
  				return new LuaTable.NormalEntry(key, value);
  			} else {
  				this.key = null;
  				this.value = null;
  				return null;
  			}
  		}
  
  		public StrongSlot find(LuaValue key) {
  			StrongSlot first = first();
  			return ( first != null ) ? first.find( key ) : null;
  		}
  
  		public boolean keyeq(LuaValue key) {
  			StrongSlot first = first();
  			return ( first != null ) && first.keyeq( key );
  		}
  
  		public Slot rest() {
  			return next;
  		}
  
  		public int arraykey(int max) {
  			// Integer keys can never be weak.
  			return 0;
  		}
  
  		public Slot set(StrongSlot target, LuaValue value) {
  			LuaValue key = strongkey();
  			if ( key != null && target.find( key ) != null ) {
  				return set( value );
  			} else if ( key != null ) {
  				// Our key is still good.
  				next = next.set( target, value );
  				return this;
  			} else {
  				// our key was dropped, remove ourselves from the chain.
  				return next.set( target, value );
  			}
  		}
  
  		public Slot add( Slot entry ) {
  			next = ( next != null ) ? next.add( entry ) : entry;
  			if ( strongkey() != null && strongvalue() != null ) {
  				return this;
  			} else {
  				return next;
  			}
  		}
  
  		public Slot remove( StrongSlot target ) {
  			LuaValue key = strongkey();
  			if ( key == null ) {
  				return next.remove( target );
  			} else if ( target.keyeq( key ) ) {
  				this.value = null;
  				return this;
  			} else {
  				next = next.remove( target );
  				return this;
  			}
  		}
  
  		public Slot relink( Slot rest ) {
  			if ( strongkey() != null && strongvalue() != null ) {
  				if ( rest == null && this.next == null ) {
  					return this;
  				} else {
  					return copy( rest );
  				}
  			} else {
  				return rest;
  			}
  		}
  
  		public LuaValue strongkey() {
  			return (LuaValue) key;
  		}
  
  		public LuaValue strongvalue() {
  			return (LuaValue) value;
  		}
  
  		protected abstract WeakSlot copy( Slot next );
  	}
  
  	static class WeakKeySlot extends WeakSlot {
  
  		private final int keyhash;
  
  		protected WeakKeySlot( LuaValue key, LuaValue value, Slot next ) {
  			super(weaken(key), value, next);
  			keyhash = key.hashCode();
  		}
  
  		protected WeakKeySlot( WeakKeySlot copyFrom, Slot next ) {
  			super( copyFrom.key, copyFrom.value, next );
  			this.keyhash = copyFrom.keyhash;
  		}
  
  		public int keyindex( int mask ) {
  			return LuaTable.hashmod( keyhash, mask );
  		}
  
  		public Slot set(LuaValue value) {
  			this.value = value;
  			return this;
  		}
  
  		public LuaValue strongkey() {
  			return strengthen( key );
  		}
  
  		protected WeakSlot copy( Slot rest ) {
  			return new WeakKeySlot( this, rest );
  		}
  	}
  
  	static class WeakValueSlot extends WeakSlot {
  
  		protected WeakValueSlot( LuaValue key, LuaValue value, Slot next ) {
  			super( key, weaken(value), next);
  		}
  
  		protected WeakValueSlot( WeakValueSlot copyFrom, Slot next ) {
  			super( copyFrom.key, copyFrom.value, next );
  		}
  
  		public int keyindex( int mask ) {
  			return LuaTable.hashSlot( strongkey(), mask );
  		}
  
  		public Slot set(LuaValue value) {
  			this.value = weaken(value);
  			return this;
  		}
  
  		public LuaValue strongvalue() {
  			return strengthen( value );
  		}
  
  		protected WeakSlot copy(Slot next) {
  			return new WeakValueSlot( this, next );
  		}
  	}
  
  	static class WeakKeyAndValueSlot extends WeakSlot {
  
  		private final int keyhash;
  
  		protected WeakKeyAndValueSlot( LuaValue key, LuaValue value, Slot next ) {
  			super( weaken(key), weaken(value), next );
  			keyhash = key.hashCode();
  		}
  
  		protected WeakKeyAndValueSlot(WeakKeyAndValueSlot copyFrom, Slot next) {
  			super( copyFrom.key, copyFrom.value, next );
  			keyhash = copyFrom.keyhash;
  		}
  
  		public int keyindex( int hashMask ) {
  			return LuaTable.hashmod( keyhash, hashMask );
  		}
  
  		public Slot set(LuaValue value) {
  			this.value = weaken(value);
  			return this;
  		}
  
  		public LuaValue strongkey() {
  			return strengthen( key );
  		}
  
  		public LuaValue strongvalue() {
  			return strengthen( value );
  		}
  
  		protected WeakSlot copy( Slot next ) {
  			return new WeakKeyAndValueSlot( this, next );
  		}
  	}
  
  	/**
  	 * Self-sent message to convert a value to its weak counterpart
  	 * @param value value to convert
  	 * @return {@link LuaValue} that is a strong or weak reference, depending on type of {@code value}
  	 */
  	protected static LuaValue weaken( LuaValue value ) {
  		switch ( value.type() ) {
  			case LuaValue.TFUNCTION:
  			case LuaValue.TTHREAD:
  			case LuaValue.TTABLE:
  				return new WeakValue(value);
  			case LuaValue.TUSERDATA:
  				return new WeakUserdata(value);
  			default:
  				return value;
  		}
  	}
  
  	/**
  	 * Unwrap a LuaValue from a WeakReference and/or WeakUserdata.
  	 * @param ref reference to convert
  	 * @return LuaValue or null
  	 * @see #weaken(LuaValue)
  	 */
  	protected static LuaValue strengthen(Object ref) {
  		if ( ref instanceof WeakReference ) {
  			ref = ((WeakReference) ref).get();
  		}
  		if ( ref instanceof WeakValue ) {
  			return ((WeakValue) ref).strongvalue();
  		}
  		return (LuaValue) ref;
  	}
  
  	/** Internal class to implement weak values. 
  	 * @see WeakTable
  	 */
  	static class WeakValue extends LuaValue {
  		WeakReference ref;
  
  		protected WeakValue(LuaValue value) {
  			ref = new WeakReference(value);
  		}
  
  		public int type() {
  			illegal("type","weak value");
  			return 0;
  		}
  
  		public String typename() {
  			illegal("typename","weak value");
  			return null;
  		}
  
  		public String toString() {
  			return "weak<"+ref.get()+">";
  		}
  
  		public LuaValue strongvalue() {
  			Object o = ref.get();
  			return (LuaValue)o;
  		}
  
  		public boolean raweq(LuaValue rhs) {
  			Object o = ref.get();
  			return o!=null && rhs.raweq((LuaValue)o);
  		}
  	}
  
  	/** Internal class to implement weak userdata values. 
  	 * @see WeakTable
  	 */
  	static final class WeakUserdata extends WeakValue {
  		private final WeakReference ob;
  		private final LuaValue mt;
  
  		private WeakUserdata(LuaValue value) {
  			super(value);
  			ob = new WeakReference(value.touserdata());
  			mt = value.getmetatable();
  		}
  
  		public LuaValue strongvalue() {
  			Object u = ref.get();
  			if ( u != null )
  				return (LuaValue) u;
  			Object o = ob.get();
  			if ( o != null ) {
  				LuaValue ud = LuaValue.userdataOf(o,mt);
  				ref = new WeakReference(ud);
  				return ud;
  			} else {
  				return null;
  			}
  		}
  	}
  
  	public LuaValue wrap(LuaValue value) {
  		return weakvalues ? weaken( value ) : value;
  	}
  
  	public LuaValue arrayget(LuaValue[] array, int index) {
  		LuaValue value = array[index];
  		if (value != null) {
  			value = strengthen(value);
  			if (value == null) {
  				array[index] = null;
  			}
  		}
  		return value;
  	}
  }
  