/*
 * FSPush.java
 * Transform
 * 
 * Copyright (c) 2001-2008 Flagstone Software Ltd. 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 Flagstone Software Ltd. 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.
 */

package com.flagstone.transform;

import java.util.*;

/**
 * FSPush is used to push values on the Flash Player's internal stack.
 * 
 * <table class="datasheet">
 * 
 * <tr>
 * <th align="left" colspan="2">Attributes</th>
 * </tr>
 * 
 * <tr>
 * <td>type</td>
 * <td>Identifies the action when it is encoded. Read-only.</td>
 * </tr>
 * 
 * <tr>
 * <td>values</td>
 * <td>An array of values that will be pushed onto the Flash Player's Stack and
 * used when executing actions.</td>
 * </tr>
 * </table>
 * 
 * <p>
 * The FSPush action supports the full range of data types supported by Flash:
 * </p>
 * 
 * <table class="datasheet">
 * <tr>
 * <th align="left">Data Type</th>
 * <th align="left">Description</th>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Boolean</td>
 * <td>A boolean value, 1 (true) or 0 (false).</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Integer</td>
 * <td>A signed 32-bit integer, range -2,147,483,648 to 2,147,483,647.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Double</td>
 * <td>A double-precision (64-bit) floating-point number, range approximately
 * +/- 1.79769313486231570E+308.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">String</td>
 * <td>A String. The string is encoded as using the UTF-8 encoding which is
 * backward compatible with ASCII encoding supported in Flash 5.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Register Index</td>
 * <td>The number (0,..3) of one of the Flash player's internal registers.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Table Index</td>
 * <td>An index into a table of string literals defined using the FSTable
 * action.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Null</td>
 * <td>A null value.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Void</td>
 * <td>A void value.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Movie Clip Property</td>
 * <td>A reserved number used to identify a specific property of a movie clip.</td>
 * </tr>
 * <tr>
 * <td nowrap width="20%">Player Property</td>
 * <td>A reserved number used to identify a specific property of the Flash
 * Player.</td>
 * </tr>
 * </table>
 * 
 * <p>
 * Movie Clip properties are used to access the attributes of the specified
 * movie clip. To simplify using movie clip properties the values are defined as
 * constants in the FSPush class:
 * </p>
 * 
 * <table class="datasheet">
 * <tr>
 * <th nowrap align="left">Name</th>
 * <th align="left">Description</th>
 * </tr>
 * <tr>
 * <td>_x</td>
 * <td>The x-coordinate of the current drawing point.</td>
 * </tr>
 * <tr>
 * <td>_y</td>
 * <td>The y-coordinate of the current drawing point.</td>
 * </tr>
 * <tr>
 * <td>_xscale</td>
 * <td>The scale of the player window in the x-axis.</td>
 * </tr>
 * <tr>
 * <td>_yscale</td>
 * <td>The scale of the player window in the y-axis.</td>
 * </tr>
 * <tr>
 * <td>_currentframe</td>
 * <td>The number of the frame currently being displayed</td>
 * </tr>
 * <tr>
 * <td>_totalframes</td>
 * <td>The total number of frames in the current movie clip being played.</td>
 * </tr>
 * <tr>
 * <td>_alpha</td>
 * <td>Whether the player supports alpha channel transparency in colours.</td>
 * </tr>
 * <tr>
 * <td>_visible</td>
 * <td>Whether the player is currently visible.</td>
 * </tr>
 * <tr>
 * <td>_width</td>
 * <td>The width of the player, in pixels, on the screen.</td>
 * </tr>
 * <tr>
 * <td>_height</td>
 * <td>The height of the player, in pixels, on the screen.</td>
 * </tr>
 * <tr>
 * <td>_rotation</td>
 * <td>The rotation of the movie clip in degrees</td>
 * </tr>
 * <tr>
 * <td>_target</td>
 * <td>The current target or context for actions.</td>
 * </tr>
 * <tr>
 * <td>_framesloaded</td>
 * <td>The number of frames from the movie clip that are currently loaded</td>
 * </tr>
 * <tr>
 * <td>_name</td>
 * <td>The name of a movie clip.</td>
 * </tr>
 * <tr>
 * <td>_droptarget</td>
 * <td>Then name of the movie clip currently being dragged.</td>
 * </tr>
 * <tr>
 * <td>_url</td>
 * <td>The URL of the current movie clip being played.</td>
 * </tr>
 * </table>
 * 
 * <p>
 * Player properties are used to access the attributes of the Player showing the
 * movie clips. To simplify using player properties the values are defined as
 * constants in the FSPush class:
 * </p>
 * 
 * <table class="datasheet">
 * <tr>
 * <th nowrap align="left">Name</th>
 * <th align="left">Description</th>
 * </tr>
 * <tr>
 * <td>_quality</td>
 * <td>Whether the player is set to display movie clips at high quality.</td>
 * </tr>
 * <tr>
 * <td>_highquality</td>
 * <td>Whether the player is set to display movie clips at high quality.</td>
 * </tr>
 * <tr>
 * <td>_focusrect</td>
 * <td>Whether a bounding yellow rectangle is drawn around the current object
 * (a sprite or button) has the keyboard and mouse focus.</td>
 * </tr>
 * <tr>
 * <td>_soundbuftime</td>
 * <td>The number of seconds to buffer streaming sound before playing</td>
 * </tr>
 * <tr>
 * <td>_xmouse</td>
 * <td>The current x-coordinate of the mouse location.</td>
 * </tr>
 * <tr>
 * <td>_ymouse</td>
 * <td>The current y-coordinate of the mouse location.</td>
 * </tr>
 * </table>
 * 
 * <p>
 * The FSPush class maintains an array of values that will be pushed onto the
 * stack. Any basic data types: boolean, int and double passed in the class
 * constructors or using the add() methods are wrapped internally, in an
 * Boolean, Integer and Double class respectively - simplifying how the class is
 * used. Wrapper classes are only used explicitly when parsing a Flash files and
 * manipulating the values stored in the array.
 * </p>
 * 
 * <h1 class="datasheet">Examples</h1>
 * 
 * <p>
 * Push each of the different types value onto the stack:
 * </p>
 * 
 * <pre>
 *  FSPush boolean        = new FSPush(true);
 *  FSPush integer        = new FSPush(123);
 *  FSPush double         = new FSPush(123.0);
 *  FSPush string         = new FSPush(&quot;123&quot;);
 *  FSPush null           = new FSPush(new FSNull());
 *  FSPush void           = new FSPush(new FSVoid());
 *  FSPush registerIndex  = new FSPush(new FSRegisterIndex(0));
 *  FSPush tableIndex     = new FSPush(new FSTableIndex(0));
 *  FSPush movieProperty  = new FSPush(FSPush._x);
 *  FSPush playerProperty = new FSPush(FSPush._xmouse);
 * </pre>
 * 
 * <p>
 * For efficiency a single FSPush action can push several values onto the stack
 * in a single operation. The order in which values are added to the FSPush
 * action is the same order in which the values are pushed onto the stack.
 * </p>
 * 
 * <pre>
 * FSPush push = new FSPush();
 * 
 * push.add(123);
 * push.add(123.0);
 * push.add(&quot;123&quot;);
 * </pre>
 * 
 * <p>
 * If an array of values are added to a FSPush object then boolean, int and
 * double values must be wrapped using the Boolean, Integer and Double classes
 * respectively.
 * </p>
 * 
 * <h1 class="datasheet">History</h1>
 * 
 * <p>
 * FSPush is a class for representing the Push action of the Macromedia Flash
 * (SWF) File Format Specification. It was introduced in Flash 4 and contained
 * either an integer or string argument. It was extended in Flash 5 to support a
 * number of different data types and allow more than one value to be added in a
 * single action.
 * </p>
 */
public class FSPush extends FSActionObject
{
	/** The x-origin of the movie clip relative to the parent clip. */
	public static final int _x = (Transform.VERSION < 5) ? 0x00000000 : 0;

	/** The y-origin of the movie clip relative to the parent clip. */
	public static final int _y = (Transform.VERSION < 5) ? 0x3f800000 : 1;

	/** The scaling factor of the movie clip in the x direction. */
	public static final int _xscale = (Transform.VERSION < 5) ? 0x40000000 : 2;

	/** The scaling factor of the movie clip in the x direction. */
	public static final int _yscale = (Transform.VERSION < 5) ? 0x40400000 : 3;

	/** The number of the current frame playing in the movie clip. */
	public static final int _currentframe = (Transform.VERSION < 5) ? 0x40800000 : 4;

	/** The total number of frames in the movie clip. */
	public static final int _totalframes = (Transform.VERSION < 5) ? 0x40a00000 : 5;

	/** The transparency of the movie clip. */
	public static final int _alpha = (Transform.VERSION < 5) ? 0x40c00000 : 6;

	/** Whether the movie clip is visible. */
	public static final int _visible = (Transform.VERSION < 5) ? 0x40e00000 : 7;

	/** The width of the movie clip in pixels. */
	public static final int _width = (Transform.VERSION < 5) ? 0x41000000 : 8;

	/** The height of the movie clip in pixels. */
	public static final int _height = (Transform.VERSION < 5) ? 0x41100000 : 9;

	/** The angle of rotation of the movie clip in degrees. */
	public static final int _rotation = (Transform.VERSION < 5) ? 0x41200000 : 10;

	/** The path of the movie clip relative to the root movie in the Player. */
	public static final int _target = (Transform.VERSION < 5) ? 0x41300000 : 11;

	/** The number of frames form the movie clip loaded. */
	public static final int _framesloaded = (Transform.VERSION < 5) ? 0x41400000 : 12;

	/** The name of movie clip. */
	public static final int _name = (Transform.VERSION < 5) ? 0x41500000 : 13;

	/** The name of the movie clip currently being dragged */
	public static final int _droptarget = (Transform.VERSION < 5) ? 0x41600000 : 14;

	/** The URL from which the movie clip was loaded. */
	public static final int _url = (Transform.VERSION < 5) ? 0x41700000 : 15;

	/** Identifies the level of aliasing being performed by the Player. */
	public static final int _highquality = (Transform.VERSION < 5) ? 0x41800000 : 16;

	/**
	 * Identifies whether a yellow rectangle is drawn around a button or test
	 * field that has the current focus.
	 */
	public static final int _focusrect = (Transform.VERSION < 5) ? 0x41880000
					: 17;

	/**
	 * The amount of time streaming sound is buffered by the Player before
	 * playing.
	 */
	public static final int _soundbuftime = (Transform.VERSION < 5) ? 0x41900000
					: 18;

	/** Identifies the level of rendering quality being performed by the Player. */
	public static final int _quality = 19;

	/** The current x-coordinate of the mouse pointer on the Player screen. */
	public static final int _xmouse = 20;

	/** The current y-coordinate of the mouse pointer on the Player screen. */
	public static final int _ymouse = 21;

	private static HashMap propertyNames = new HashMap();

	static
	{
		propertyNames.put("_x", new Integer(_x));
		propertyNames.put("_y", new Integer(_y));
		propertyNames.put("_xscale", new Integer(_xscale));
		propertyNames.put("_yscale", new Integer(_yscale));
		propertyNames.put("_currentframe", new Integer(_currentframe));
		propertyNames.put("_totalframes", new Integer(_totalframes));
		propertyNames.put("_alpha", new Integer(_alpha));
		propertyNames.put("_visible", new Integer(_visible));
		propertyNames.put("_width", new Integer(_width));
		propertyNames.put("_height", new Integer(_height));
		propertyNames.put("_rotation", new Integer(_rotation));
		propertyNames.put("_target", new Integer(_target));
		propertyNames.put("_framesloaded", new Integer(_framesloaded));
		propertyNames.put("_name", new Integer(_name));
		propertyNames.put("_droptarget", new Integer(_droptarget));
		propertyNames.put("_url", new Integer(_url));
		propertyNames.put("_highquality", new Integer(_highquality));
		propertyNames.put("_focusrect", new Integer(_focusrect));
		propertyNames.put("_soundbuftime", new Integer(_soundbuftime));
		propertyNames.put("_quality", new Integer(_quality));
		propertyNames.put("_xmouse", new Integer(_xmouse));
		propertyNames.put("_ymouse", new Integer(_ymouse));
	}

	/**
	 * Translates the name of a property into the integer value used to identify
	 * the property by the Flash Player.
	 * 
	 * @param propertyName
	 *            a String representing the name of the property.
	 * @return the value of a property identifier.
	 */
	public static Integer propertyWithName(String propertyName)
	{
		Integer identifier = null;

		if (propertyNames.containsKey(propertyName))
			identifier = (Integer) propertyNames.get(propertyName);

		return identifier;
	}

	private ArrayList values = new ArrayList();

	/**
	 * Construct an FSPush object, initialising it with values decoded from an
	 * encoded object.
	 * 
	 * @param coder
	 *            an FSCoder containing the binary data.
	 */
	public FSPush(FSCoder coder)
	{
		super(Push);
		decode(coder);
	}

	/** 
	 * Constructs an FSPush action object with no values added. 
	 */
	public FSPush()
	{
		super(Push);
	}

	/**
	 * Constructs an FSPush action that will place the specified boolean value
	 * on the stack.
	 * 
	 * @param aBoolean
	 *            boolean value to push onto the stack.
	 */
	public FSPush(boolean aBoolean)
	{
		super(Push);
		add(aBoolean);
	}

	/**
	 * Constructs an FSPush action that will place the specified integer on the
	 * stack.
	 * 
	 * @param aNumber
	 *            integer value to push onto the stack.
	 */
	public FSPush(int aNumber)
	{
		super(Push);
		add(aNumber);
	}

	/**
	 * Constructs an FSPush action that will place the specified double value on
	 * the stack.
	 * 
	 * @param aNumber
	 *            double-precision floating-point value to push onto the stack.
	 */
	public FSPush(double aNumber)
	{
		super(Push);
		add(aNumber);
	}

	/**
	 * Constructs an FSPush action that will place a null value on the stack.
	 * 
	 * @param nullValue
	 *            an instance of the simple FSNull class.
	 */
	public FSPush(FSNull nullValue)
	{
		super(Push);
		add(nullValue);
	}

	/**
	 * Constructs an FSPush action that will place a void value on the stack.
	 * 
	 * @param voidValue
	 *            an instance of the simple FSVoid class.
	 */
	public FSPush(FSVoid voidValue)
	{
		super(Push);
		add(voidValue);
	}

	/**
	 * Constructs an FSPush action that will place the specified value on the
	 * stack.
	 * 
	 * @param aString
	 *            string literal value to push onto the stack.
	 */
	public FSPush(String aString)
	{
		super(Push);
		add(aString);
	}

	/**
	 * Constructs an FSPush action that reference a variable in a table.
	 * 
	 * @param anIndex
	 *            an index into a literal table to push onto the stack.
	 */
	public FSPush(FSTableIndex anIndex)
	{
		super(Push);
		add(anIndex);
	}

	/**
	 * Constructs an FSPush action that references one of the players internal
	 * registers.
	 * 
	 * @param anIndex
	 *            a reference to one of the Flash Player's internal registers to
	 *            push onto the stack.
	 */
	public FSPush(FSRegisterIndex anIndex)
	{
		super(Push);
		add(anIndex);
	}

	/**
	 * Constructs an FSPush action that will push the values in the array onto
	 * the stack.
	 * 
	 * @param anArray
	 *            an array of values to be pushed onto the stack. The values in
	 *            the array must be one of the following classes: Boolean,
	 *            Integer, Double, String, FSRegisterIndex or FSTableIndex.
	 */
	public FSPush(ArrayList anArray)
	{
		super(Push);
		setValues(anArray);
	}

	/**
	 * Constructs an FSPush object by copying values from an existing object.
	 * 
	 * @param obj
	 *            an FSPush object.
	 */
	public FSPush(FSPush obj)
	{
		super(obj);

		values = new ArrayList(obj.values.size());

		for (Iterator i = obj.values.iterator(); i.hasNext();)
		{
			Object value = i.next();

			if (value instanceof Boolean)
				values.add(new Boolean(((Boolean) value).booleanValue()));
			else if (value instanceof Integer)
				values.add(new Integer(((Integer) value).intValue()));
			else if (value instanceof Float)
				values.add(new Float(((Float) value).floatValue()));
			else if (value instanceof Double)
				values.add(new Double(((Double) value).doubleValue()));
			else if (value instanceof String)
				values.add(new String((String) value));
			else if (value instanceof FSProperty)
				values.add(new FSProperty((FSProperty) value));
			else if (value instanceof FSNull)
				values.add(((FSNull) value).clone());
			else if (value instanceof FSVoid)
				values.add(((FSVoid) value).clone());
			else if (value instanceof FSTableIndex)
				values.add(((FSTableIndex) value).clone());
			else if (value instanceof FSRegisterIndex)
				values.add(((FSRegisterIndex) value).clone());
		}
	}

	/**
	 * Adds a boolean value to the array of values that will be pushed onto the
	 * stack.
	 * 
	 * @param aBool
	 *            a boolean value.
	 */
	public void add(boolean aBool)
	{
		values.add(new Boolean(aBool));
	}

	/**
	 * Adds an integer value to the array of values that will be pushed onto the
	 * stack.
	 * 
	 * @param anInt
	 *            an integer (int) value.
	 */
	public void add(int anInt)
	{
		values.add(new Integer(anInt));
	}

	/**
	 * Adds a double value to the array of values that will be pushed onto the
	 * stack.
	 * 
	 * @param aDouble
	 *            a double-precision floating-point value.
	 */
	public void add(double aDouble)
	{
		values.add(new Double(aDouble));
	}

	/**
	 * Adds a null value to the array of values that will be pushed onto the
	 * stack.
	 * 
	 * @param nullValue
	 *            a lightweight FSNull object.
	 */
	public void add(FSNull nullValue)
	{
		values.add(nullValue);
	}

	/**
	 * Adds a void value to the array of values that will be pushed onto the
	 * stack.
	 * 
	 * @param voidValue
	 *            a lightweight FSVoid object.
	 */
	public void add(FSVoid voidValue)
	{
		values.add(voidValue);
	}

	/**
	 * Adds a String to the array of values that will be pushed onto the stack.
	 * 
	 * @param aString
	 *            a String.
	 */
	public void add(String aString)
	{
		values.add(aString);
	}

	/**
	 * Adds an FSTableIndex to the array of values that will be pushed onto the
	 * stack.
	 * 
	 * @param anIndex
	 *            a FSTableIndex referencing an entry in a table of literals.
	 */
	public void add(FSTableIndex anIndex)
	{
		values.add(anIndex);
	}

	/**
	 * Adds an FSRegisterIndex to the array of values that will be pushed onto
	 * the stack.
	 * 
	 * @param anIndex
	 *            a FSRegisterIndex referencing one of the Flash Player's
	 *            internal registers.
	 */
	public void add(FSRegisterIndex anIndex)
	{
		values.add(anIndex);
	}

	/**
	 * Gets the array of values.
	 * 
	 * @return the array of values that will be pushed onto the stack.
	 */
	public ArrayList getValues()
	{
		return values;
	}

	/**
	 * Sets the array of values.
	 * 
	 * @param anArray
	 *            replaces the existing array of value with anArray. The values
	 *            in the array must be one of the following classes: Boolean,
	 *            Integer, Double, String, FSNull, FSVoid, FSRegisterIndex or
	 *            FSTableIndex.
	 */
	public void setValues(ArrayList anArray)
	{
		values = anArray;
	}

	public Object clone()
	{
		FSPush anObject = (FSPush) super.clone();

		anObject.values = new ArrayList(values.size());

		for (Iterator i = values.iterator(); i.hasNext();)
		{
			Object obj = i.next();

			if (obj instanceof Boolean)
				anObject.values
								.add(new Boolean(((Boolean) obj).booleanValue()));
			else if (obj instanceof Integer)
				anObject.values.add(new Integer(((Integer) obj).intValue()));
			else if (obj instanceof Float)
				anObject.values.add(new Float(((Float) obj).floatValue()));
			else if (obj instanceof Double)
				anObject.values.add(new Double(((Double) obj).doubleValue()));
			else if (obj instanceof String)
				anObject.values.add(new String((String) obj));
			else if (obj instanceof FSProperty)
				anObject.values.add(new FSProperty((FSProperty) obj));
			else if (obj instanceof FSNull)
				anObject.values.add(((FSNull) obj).clone());
			else if (obj instanceof FSVoid)
				anObject.values.add(((FSVoid) obj).clone());
			else if (obj instanceof FSTableIndex)
				anObject.values.add(((FSTableIndex) obj).clone());
			else if (obj instanceof FSRegisterIndex)
				anObject.values.add(((FSRegisterIndex) obj).clone());
		}

		return anObject;
	}

	public boolean equals(Object anObject)
	{
		boolean result = false;

		if (super.equals(anObject))
			result = values.equals(((FSPush) anObject).values);

		return result;
	}

	public void appendDescription(StringBuffer buffer, int depth)
	{
		buffer.append(name());

		if (depth > 0)
		{
			buffer.append(": { ");
			Transform.append(buffer, "values", values, depth);
			buffer.append("}");
		}
	}

	public int length(FSCoder coder)
	{
		super.length(coder);

		for (Iterator i = values.iterator(); i.hasNext();)
		{
			Object anObject = i.next();

			if (anObject instanceof Boolean)
				length += 2;
			else if (anObject instanceof FSProperty)
				length += 5;
			else if (anObject instanceof Integer)
				length += 5;
			else if (anObject instanceof Double)
				length += 9;
			else if (anObject instanceof String)
				length += 1 + coder.strlen((String) anObject, true);
			else if (anObject instanceof FSNull)
				length += 1;
			else if (anObject instanceof FSVoid)
				length += 1;
			else if (anObject instanceof FSTableIndex)
			{
				if (((FSTableIndex) anObject).getIndex() < 256)
					length += 2;
				else
					length += 3;
			} else if (anObject instanceof FSRegisterIndex)
				length += 2;
		}

		return length;
	}

	public void encode(FSCoder coder)
	{
		super.encode(coder);

		for (Iterator i = values.iterator(); i.hasNext();)
		{
			Object anObject = i.next();

			if (anObject instanceof Boolean)
			{
				coder.writeWord(5, 1);
				coder.writeWord(((Boolean) anObject).booleanValue() ? 1 : 0, 1);
			} else if (anObject instanceof Integer)
			{
				coder.writeWord(7, 1);
				coder.writeWord(((Integer) anObject).intValue(), 4);
			} else if (anObject instanceof FSProperty)
			{
				coder.writeWord(1, 1);
				coder.writeWord(((FSProperty) anObject).getValue(), 4);
			} else if (anObject instanceof Double)
			{
				coder.writeWord(6, 1);
				coder.writeDouble(((Double) anObject).doubleValue());
			} else if (anObject instanceof String)
			{
				coder.writeWord(0, 1);
				coder.writeString((String) anObject);
				coder.writeWord(0, 1);
			} else if (anObject instanceof FSNull)
			{
				coder.writeWord(2, 1);
			} else if (anObject instanceof FSVoid)
			{
				coder.writeWord(3, 1);
			} else if (anObject instanceof FSTableIndex)
			{
				if (((FSTableIndex) anObject).getIndex() < 256)
				{
					coder.writeWord(8, 1);
					coder.writeWord(((FSTableIndex) anObject).getIndex(), 1);
				} else
				{
					coder.writeWord(9, 1);
					coder.writeWord(((FSTableIndex) anObject).getIndex(), 2);
				}
			} else if (anObject instanceof FSRegisterIndex)
			{
				coder.writeWord(4, 1);
				coder.writeWord(((FSRegisterIndex) anObject).getIndex(), 1);
			}
		}

		coder.endObject(name());
	}

	public void decode(FSCoder coder)
	{
		super.decode(coder);

		int valuesLength = length;

		while (valuesLength > 0)
		{
			int dataType = coder.scanWord(1, false);

			coder.adjustPointer(8);

			switch (dataType)
			{
				case 0:
					int start = coder.getPointer();
					int strlen = 0;

					for (strlen = 0; coder.scanWord(1, false) != 0; coder
									.adjustPointer(8), strlen++)
						;

					coder.setPointer(start);
					values.add(coder.readString(strlen));
					coder.adjustPointer(8);
					valuesLength -= strlen + 2;
					break;
				case 1: // Pre version 5 property
					int propertyValue = coder.readWord(4, false);
					values.add(new FSProperty(propertyValue));
					valuesLength -= 5;
					break;
				case 2:
					values.add(FSNull.getInstance());
					valuesLength -= 1;
					break;
				case 3:
					values.add(FSVoid.getInstance());
					valuesLength -= 1;
					break;
				case 4:
					int registerIndex = coder.readWord(1, false);
					values.add(new FSRegisterIndex(registerIndex));
					valuesLength -= 2;
					break;
				case 5:
					boolean boolValue = coder.readWord(1, false) != 0 ? true
									: false;
					values.add(new Boolean(boolValue));
					valuesLength -= 2;
					break;
				case 6:
					double doubleValue = coder.readDouble();
					values.add(new Double(doubleValue));
					valuesLength -= 9;
					break;
				case 7:
					int intValue = coder.readWord(4, false);
					values.add(new Integer(intValue));
					valuesLength -= 5;
					break;
				case 8:
				case 9:
					int tableIndex = coder.readWord(dataType == 8 ? 1 : 2,
									false);
					values.add(new FSTableIndex(tableIndex));
					valuesLength -= (dataType == 8) ? 2 : 3;
					break;
			}
		}

		coder.endObject(name());
	}
}
