/*
#
# Copyright (c) 2009 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any waranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
*/

package tests
{
import com.rpath.xobj.*;

import tests.models.*;

import flash.xml.XMLDocument;

public class TestBasics extends TestBase
{
    /** testSimple
    * test basic decoding behavior of a simple XML document
    */
    public function testSimple():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder();
        var xmlInput:XMLDocument = new XMLDocument(testData.simple);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue("top is object", o.top is Object);
        assertTrue("attr1 value", o.top.attr1 == "anattr");
        assertTrue("attr2 value", o.top.attr2 == "another");
        assertTrue("prop value", o.top.prop == "something");
        assertTrue("subelement.subaattr value", o.top.subelement.subattr == "2");
        assertTrue("subelement is Object", o.top.subelement is Object);
    }

    /** testComplex
    * Slightly more complex test with a repeated element that results in an
    * Array property
    */
    public function testComplex():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder();
        var xmlInput:XMLDocument = new XMLDocument(testData.complex);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top is Object);
        assertTrue(o.top.prop.subprop is Array);

        // check array content
        for (var i:int=0; i<o.top.prop.subprop.length; i++)
        {
            assertTrue(o.top.prop.subprop[i] is XObjString);
            assertTrue(o.top.prop.subprop[i].toString() == ['asdf', 'fdsa', 'zxcv '][i]);
        }

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder();
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.top, null, "top");

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    /** testNamespaces
    * test namespaces and prefixes are correctly mapped to properties on decode
    * and that they are correctly mapped back to prefixed tags on encode.
    * Also check that encoding is symmetrical with decoding.
    * This particular test case has an element tag that defines the namespace
    * that itself is declared within - a specific edge case.
    */
    public function testNamespaces():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder();
        var xmlInput:XMLDocument = new XMLDocument(testData.namespaces);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top.other_tag.other_val == '1')
        assertTrue(o.top.other2_tag.val == '2')

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder();
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.top, null, "top");

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    /** testMappedNamespaces
    * test that we can specify a local prefix other3 for a given namespace
    * allowing us to write code that is insulated from the arbitrary prefixes
    * a given document might choose.
    */
    public function testMappedNamespaces():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(null, { other3 : 'http://other/other2'});
        var xmlInput:XMLDocument = new XMLDocument(testData.namespaces);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top.other_tag.other_val == '1')
        assertTrue(o.top.other3_tag.val == '2')

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder();
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.top, null, "top");

        // check that we remap to original prefixes on the way back out
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    /** testExplicitNamespace
    * test that when a namespace is redundantly declared as both default and
    * by prefix in the XML, that the result is strictly prefixed on output
    * This is to ensure documents conformant to an XMLSchema that uses
    * attributeFormDefault = qualified and elementFormDefault = qualified
    * are encoded correctly for the schema.
    *
    * Note that we *always* do this, since an unqualified schema requirement
    * will be satisified by a qualified XML document.
    */
    public function testExplicitNamespace():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder();
        var xmlInput:XMLDocument = new XMLDocument(testData.explicitns);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.ns_top.ns_element.ns_attr == 'foo')

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder();
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.ns_top, null, "ns_top", o._xobj.elements[0].qname);

        var expectedString:String =
                '<ns:top xmlns:ns="http://somens.xsd">' +
                '<ns:element ns:attr="foo"/>' +
                '</ns:top>';

        // check that we remap to original prefixes on the way back out
        assertTrue("encode is fully qualified", compareXMLtoString(xmlOutput, expectedString));
    }

    /** testObjectTree
    * test that we can construct a new object graph and encode it as XML.
    * test that we can then decode it and get back a new graph with correctly
    * typed ActionScript objects.
    */
    public function testObjectTree():void
    {
        var t:Top = new Top();
        t.prop = 'abc';
        t.middle = new Middle();
        t.middle.tag = 123;
        t.bottom = null;
        var typeMap:* = {top:Top};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(t);

        var expectedString:String =
                '<top>'+
                '<bottom/>'+
                '<middle>'+
                '<tag>123</tag>'+
                '</middle>'+
                '<prop>abc</prop>'+
                '</top>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top is Top);
        assertTrue(o.top.middle is Middle);
        assertTrue(o.top.middle.tag == 123);
        assertTrue(o.top.middle.foo() == 123);
        assertTrue(o.top.bottom == null);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.top);

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));

    }

    /** testObjectTreeNulls
     * test that we can construct a new object graph and encode it as XML.
     * test that we can then decode it and get back a new graph with correctly
     * typed ActionScript objects.
     */
    public function testObjectTreeNulls():void
    {
        var t:Top = new Top();
        t.prop = 'abc';
        t.middle = new Middle();
        t.middle.tag = 123;
        t.bottom = null;
        var typeMap:* = {top:Top};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        typedEncoder.encodeNullElements = false;
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(t);

        var expectedString:String =
            '<top>'+
            '<middle>'+
            '<tag>123</tag>'+
            '</middle>'+
            '<prop>abc</prop>'+
            '</top>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top is Top);
        assertTrue(o.top.middle is Middle);
        assertTrue(o.top.middle.tag == 123);
        assertTrue(o.top.middle.foo() == 123);
        assertTrue(o.top.bottom == null);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.top);

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));

    }

    public function testId():void
    {
    }

    /**
     * Ensure boolean data is handled properly
     */
    public function testBoolean():void
    {
        var obj:TestableObject = new TestableObject();
        obj.someVal = "someval";
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someVal>someval</someVal>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableObject);
        assertTrue(o.obj.someVal =="someval");
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    public function testNumericStrings():void
    {
        var obj:TestableObject = new TestableObject();

        obj.someVal = "1.0";
        // make sure someVal is a string so this is a valid test
        assertTrue(obj.someVal is String);
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someVal>1.0</someVal>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableObject);
        assertTrue(o.obj.someVal == "1.0");
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    public function testStringNumerics():void
    {
        var obj:TestableNumericObject = new TestableNumericObject();

        obj.someNumber = 1.1;
        // make sure someNumber is a Number so this is a valid test
        assertTrue(obj.someNumber is Number);
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableNumericObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someNumber>1.1</someNumber>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableNumericObject);
        assertTrue(o.obj.someNumber == 1.1);
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    public function testStringNumerics2():void
    {
        var obj:TestableNumericObject = new TestableNumericObject();

        obj.someNumber = 0.5;
        // make sure someNumber is a Number so this is a valid test
        assertTrue(obj.someNumber is Number);
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableNumericObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someNumber>0.5</someNumber>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableNumericObject);
        assertTrue(o.obj.someNumber == 0.5);
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    }
}

/*
#
# Copyright (c) 2009 rPath, Inc.
#
# This program is distributed under the terms of the MIT License as found
# in a file called LICENSE. If it is not present, the license
# is always available at http://www.opensource.org/licenses/mit-license.php.
#
# This program is distributed in the hope that it will be useful, but
# without any waranty; without even the implied warranty of merchantability
# or fitness for a particular purpose. See the MIT License for full details.
*/

package tests
{
import com.rpath.xobj.*;

import tests.models.*;

import flash.xml.XMLDocument;

public class TestBasics extends TestBase
{
    /** testSimple
    * test basic decoding behavior of a simple XML document
    */
    public function testSimple():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder();
        var xmlInput:XMLDocument = new XMLDocument(testData.simple);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue("top is object", o.top is Object);
        assertTrue("attr1 value", o.top.attr1 == "anattr");
        assertTrue("attr2 value", o.top.attr2 == "another");
        assertTrue("prop value", o.top.prop == "something");
        assertTrue("subelement.subaattr value", o.top.subelement.subattr == "2");
        assertTrue("subelement is Object", o.top.subelement is Object);
    }

    /** testComplex
    * Slightly more complex test with a repeated element that results in an
    * Array property
    */
    public function testComplex():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder({prop: Array});
        var xmlInput:XMLDocument = new XMLDocument(testData.complex);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top is Object);
        assertTrue(o.top.prop is Array);

        // check array content
        for (var i:int=0; i<o.top.prop.length; i++)
        {
            assertTrue(o.top.prop[i] is XObjString);
            assertTrue(o.top.prop[i].toString() == ['asdf', 'fdsa', 'zxcv '][i]);
        }

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder({subprop: XObjString});
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.top, null, "top");

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    /** testNamespaces
    * test namespaces and prefixes are correctly mapped to properties on decode
    * and that they are correctly mapped back to prefixed tags on encode.
    * Also check that encoding is symmetrical with decoding.
    * This particular test case has an element tag that defines the namespace
    * that itself is declared within - a specific edge case.
    */
    public function testNamespaces():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder();
        var xmlInput:XMLDocument = new XMLDocument(testData.namespaces);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top.other_tag.other_val == '1')
        assertTrue(o.top.other2_tag.val == '2')

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder();
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.top, null, "top");

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    /** testMappedNamespaces
    * test that we can specify a local prefix other3 for a given namespace
    * allowing us to write code that is insulated from the arbitrary prefixes
    * a given document might choose.
    */
    public function testMappedNamespaces():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(null, { other3 : 'http://other/other2'});
        var xmlInput:XMLDocument = new XMLDocument(testData.namespaces);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top.other_tag.other_val == '1')
        assertTrue(o.top.other3_tag.val == '2')

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder();
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.top, null, "top");

        // check that we remap to original prefixes on the way back out
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    /** testExplicitNamespace
    * test that when a namespace is redundantly declared as both default and
    * by prefix in the XML, that the result is strictly prefixed on output
    * This is to ensure documents conformant to an XMLSchema that uses
    * attributeFormDefault = qualified and elementFormDefault = qualified
    * are encoded correctly for the schema.
    *
    * Note that we *always* do this, since an unqualified schema requirement
    * will be satisified by a qualified XML document.
    */
    public function testExplicitNamespace():void
    {
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder();
        var xmlInput:XMLDocument = new XMLDocument(testData.explicitns);
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.ns_top.ns_element.ns_attr == 'foo')

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder();
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(o.ns_top, null, "ns_top", o._xobj.elements[0].qname);

        var expectedString:String =
                '<ns:top xmlns:ns="http://somens.xsd">' +
                '<ns:element ns:attr="foo"/>' +
                '</ns:top>';

        // check that we remap to original prefixes on the way back out
        assertTrue("encode is fully qualified", compareXMLtoString(xmlOutput, expectedString));
    }

    /** testObjectTree
    * test that we can construct a new object graph and encode it as XML.
    * test that we can then decode it and get back a new graph with correctly
    * typed ActionScript objects.
    */
    public function testObjectTree():void
    {
        var t:Top = new Top();
        t.prop = 'abc';
        t.middle = new Middle();
        t.middle.tag = 123;
        t.bottom = null;
        var typeMap:* = {top:Top};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(t);

        var expectedString:String =
                '<top>'+
                '<bottom/>'+
                '<middle>'+
                '<tag>123</tag>'+
                '</middle>'+
                '<prop>abc</prop>'+
                '</top>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top is Top);
        assertTrue(o.top.middle is Middle);
        assertTrue(o.top.middle.tag == 123);
        assertTrue(o.top.middle.foo() == 123);
        assertTrue(o.top.bottom == null);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.top);

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));

    }

    /** testObjectTreeNulls
     * test that we can construct a new object graph and encode it as XML.
     * test that we can then decode it and get back a new graph with correctly
     * typed ActionScript objects.
     */
    public function testObjectTreeNulls():void
    {
        var t:Top = new Top();
        t.prop = 'abc';
        t.middle = new Middle();
        t.middle.tag = 123;
        t.bottom = null;
        var typeMap:* = {top:Top};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        typedEncoder.encodeNullElements = false;
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(t);

        var expectedString:String =
            '<top>'+
            '<middle>'+
            '<tag>123</tag>'+
            '</middle>'+
            '<prop>abc</prop>'+
            '</top>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);

        assertTrue(o.top is Top);
        assertTrue(o.top.middle is Middle);
        assertTrue(o.top.middle.tag == 123);
        assertTrue(o.top.middle.foo() == 123);
        assertTrue(o.top.bottom == null);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.top);

        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));

    }

    public function testId():void
    {
    }

    /**
     * Ensure boolean data is handled properly
     */
    public function testBoolean():void
    {
        var obj:TestableObject = new TestableObject();
        obj.someVal = "someval";
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someVal>someval</someVal>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableObject);
        assertTrue(o.obj.someVal =="someval");
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    public function testNumericStrings():void
    {
        var obj:TestableObject = new TestableObject();

        obj.someVal = "1.0";
        // make sure someVal is a string so this is a valid test
        assertTrue(obj.someVal is String);
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someVal>1.0</someVal>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableObject);
        assertTrue(o.obj.someVal == "1.0");
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    public function testStringNumerics():void
    {
        var obj:TestableNumericObject = new TestableNumericObject();

        obj.someNumber = 1.1;
        // make sure someNumber is a Number so this is a valid test
        assertTrue(obj.someNumber is Number);
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableNumericObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someNumber>1.1</someNumber>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableNumericObject);
        assertTrue(o.obj.someNumber == 1.1);
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    public function testStringNumerics2():void
    {
        var obj:TestableNumericObject = new TestableNumericObject();

        obj.someNumber = 0.5;
        // make sure someNumber is a Number so this is a valid test
        assertTrue(obj.someNumber is Number);
        obj.booleanVar = true;
        var typeMap:* = {obj: TestableNumericObject};

        var typedEncoder:XObjXMLEncoder = new XObjXMLEncoder(typeMap);
        var xmlOutput:XMLDocument = typedEncoder.encodeObject(obj);

        // neither the Transient nor the xobjTransient vars should be there
        var expectedString:String =
                '<obj>'+
                '<booleanVar>true</booleanVar>'+
                '<someNumber>0.5</someNumber>'+
                '</obj>';

        assertTrue(compareXMLtoString(xmlOutput, expectedString));

        // now decode it and validate
        var typedDecoder:XObjXMLDecoder = new XObjXMLDecoder(typeMap);
        var xmlInput:XMLDocument = xmlOutput;
        var o:* = typedDecoder.decodeXML(xmlInput);
        assertTrue(o.obj is TestableNumericObject);
        assertTrue(o.obj.someNumber == 0.5);
        assertTrue(o.obj.booleanVar);

        // reencode and check round-trip
        xmlOutput = typedEncoder.encodeObject(o.obj);
        assertTrue("encode matches input", compareXML(xmlOutput, xmlInput));
    }

    }
}

Fix for testComplex

Fix for testComplex


package aerys.minko.scene.controller
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.math.Matrix4x4;

	import flash.display.BitmapData;
	import flash.utils.Dictionary;

	use namespace minko_math;

	/**
	 * The TransformController handles the batched update of all the local to world matrices
	 * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
	 * automatically disable itself on other nodes.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class TransformController extends AbstractController
	{
		private var _target					: ISceneNode;

		private var _invalidList			: Boolean;

		private var _nodeToId				: Dictionary;
		private var _idToNode				: Vector.<ISceneNode>
		private var _transforms				: Vector.<Matrix4x4>;
		private var _localToWorldTransforms : Vector.<Matrix4x4>;
		private var _numChildren			: Vector.<uint>;
		private var _firstChildId			: Vector.<uint>
		private var _parentId				: Vector.<int>

		public function TransformController()
		{
			super();

			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function renderingBeginHandler(scene			: Scene,
										  viewport		: Viewport,
										  destination	: BitmapData,
										  time			: Number) : void
		{
			if (_invalidList)
				updateTransformsList();

			if (_transforms.length)
			{
				var rootTransform 	: Matrix4x4	= _transforms[0];
				var isDirty			: Boolean	= rootTransform._hasChanged;

				if (isDirty)
				{
					_localToWorldTransforms[0].copyFrom(rootTransform);
					rootTransform._hasChanged = false;
				}

				updateLocalToWorld();
			}
		}

		private function updateLocalToWorld(nodeId : uint = 0) : void
		{
			var numNodes 			: uint 			= _transforms.length;
			var childrenOffset		: uint			= 1;
			var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
			var rootTransform		: Matrix4x4		= _transforms[nodeId];
			var root				: ISceneNode	= _idToNode[childId];

			if (rootTransform._hasChanged)
			{
				rootLocalToWorld.copyFrom(rootTransform);

				if (nodeId != 0)
					rootLocalToWorld.append(_transforms[_parentId[nodeId]]);

				root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
			}

			for (; nodeId < numNodes; ++nodeId)
			{
				var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
				var numChildren		: uint		= _numChildren[nodeId];
				var isDirty			: Boolean	= localToWorld._hasChanged;
				var firstChildId	: uint		= _firstChildId[nodeId];
				var lastChildId		: uint		= firstChildId + numChildren;

				localToWorld._hasChanged = false;

				for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
				{
					var childTransform		: Matrix4x4		= _transforms[childId];
					var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
					var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged;

					if (childIsDirty)
					{
						var child	: ISceneNode	= _idToNode[childId];

						childLocalToWorld
							.copyFrom(childTransform)
							.append(localToWorld);

						childTransform._hasChanged = false;

						child.localToWorldTransformChanged.execute(child, childLocalToWorld);
					}
				}
			}
		}

		private function updateAncestorsAndSelfLocalToWorld(nodeId : int) : void
		{
			var dirtyRoot : int = nodeId;

			while (nodeId >= 0)
			{
				if ((_transforms[nodeId] as Matrix4x4)._hasChanged)
					dirtyRoot = nodeId;

				nodeId = _parentId[nodeId];
			}

			if (dirtyRoot >= 0)
				updateLocalToWorld(dirtyRoot);
		}

		private function targetAddedHandler(ctrl	: TransformController,
											target	: ISceneNode) : void
		{
			if (_target)
				throw new Error('The TransformController cannot have more than one target.');

			_target = target;
			_invalidList = true;

			if (target is Scene)
			{
				(target as Scene).renderingBegin.add(renderingBeginHandler);
				return;
			}

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.add(descendantAddedHandler);
				targetGroup.descendantRemoved.add(descendantRemovedHandler);
			}

			target.added.add(addedHandler);
		}

		private function targetRemovedHandler(ctrl		: TransformController,
											  target	: ISceneNode) : void
		{
			target.added.remove(addedHandler);

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.remove(descendantAddedHandler);
				targetGroup.descendantRemoved.remove(descendantRemovedHandler);
			}

			_invalidList = false;
			_target = null;

			_nodeToId = null;
			_transforms = null;
			_localToWorldTransforms = null;
			_numChildren = null;
			_idToNode = null;
			_parentId = null;
		}

		private function addedHandler(target : ISceneNode, ancestor : Group) : void
		{
			// the controller will remove itself from the node when it's not its own root anymore
			// but it will watch for the 'removed' signal to add itself back if the node becomes
			// its own root again
			_target.removed.add(removedHandler);
			_target.removeController(this);
		}

		private function removedHandler(target : ISceneNode, ancestor : Group) : void
		{
			if (target.root == target)
			{
				target.removed.remove(removedHandler);
				target.addController(this);
			}
		}

		private function descendantAddedHandler(root		: Group,
												descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function descendantRemovedHandler(root			: Group,
												  descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function updateTransformsList() : void
		{
			var root 	: ISceneNode 			= _target.root;
			var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
			var nodeId 	: uint 					= 0;

			_nodeToId = new Dictionary(true);
			_transforms = new <Matrix4x4>[];
			_localToWorldTransforms = new <Matrix4x4>[];
			_numChildren = new <uint>[];
			_firstChildId = new <uint>[];
			_idToNode = new <ISceneNode>[];
			_parentId = new <int>[-1];

			while (nodes.length)
			{
				var node 	: ISceneNode 	= nodes.shift();
				var group 	: Group 		= node as Group;

				_nodeToId[node] = nodeId;
				_idToNode[nodeId] = node;
				_transforms[nodeId] = node.transform;
				_localToWorldTransforms[nodeId] = new Matrix4x4().lock();

				if (group)
				{
					var numChildren 	: uint = group.numChildren;
					var firstChildId 	: uint = nodeId + nodes.length + 1;

					_numChildren[nodeId] = numChildren;
					_firstChildId[nodeId] = firstChildId;
					for (var childId : uint = 0; childId < numChildren; ++childId)
					{
						_parentId[uint(firstChildId + childId)] = nodeId;
						nodes.push(group.getChildAt(childId));
					}
				}
				else
				{
					_numChildren[nodeId] = 0;
					_firstChildId[nodeId] = 0;
				}

				++nodeId;
			}

			_invalidList = false;
		}

		public function getLocalToWorldTransform(node 			: ISceneNode,
												 forceUpdate 	: Boolean = false) : Matrix4x4
		{
			if (_invalidList || _nodeToId[node] == undefined)
				updateTransformsList();

			var nodeId : uint = _nodeToId[node];

			if (forceUpdate)
				updateAncestorsAndSelfLocalToWorld(nodeId);

			return _localToWorldTransforms[nodeId];
		}
	}
}

package aerys.minko.scene.controller
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.math.Matrix4x4;

	import flash.display.BitmapData;
	import flash.utils.Dictionary;

	use namespace minko_math;

	/**
	 * The TransformController handles the batched update of all the local to world matrices
	 * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
	 * automatically disable itself on other nodes.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class TransformController extends AbstractController
	{
		private var _target					: ISceneNode;

		private var _invalidList			: Boolean;

		private var _nodeToId				: Dictionary;
		private var _idToNode				: Vector.<ISceneNode>
		private var _transforms				: Vector.<Matrix4x4>;
		private var _localToWorldTransforms : Vector.<Matrix4x4>;
		private var _numChildren			: Vector.<uint>;
		private var _firstChildId			: Vector.<uint>
		private var _parentId				: Vector.<int>

		public function TransformController()
		{
			super();

			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function renderingBeginHandler(scene			: Scene,
										  viewport		: Viewport,
										  destination	: BitmapData,
										  time			: Number) : void
		{
			if (_invalidList)
				updateTransformsList();

			if (_transforms.length)
			{
				var rootTransform 	: Matrix4x4	= _transforms[0];
				var isDirty			: Boolean	= rootTransform._hasChanged;

				if (isDirty)
				{
					_localToWorldTransforms[0].copyFrom(rootTransform);
					rootTransform._hasChanged = false;
				}

				updateLocalToWorld();
			}
		}

		private function updateLocalToWorld(nodeId : uint = 0) : void
		{
			var numNodes 			: uint 			= _transforms.length;
			var childrenOffset		: uint			= 1;
			var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
			var rootTransform		: Matrix4x4		= _transforms[nodeId];
			var root				: ISceneNode	= _idToNode[childId];

			if (rootTransform._hasChanged)
			{
				rootLocalToWorld.copyFrom(rootTransform);

				if (nodeId != 0)
					rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

				root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
			}

			for (; nodeId < numNodes; ++nodeId)
			{
				var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
				var numChildren		: uint		= _numChildren[nodeId];
				var isDirty			: Boolean	= localToWorld._hasChanged;
				var firstChildId	: uint		= _firstChildId[nodeId];
				var lastChildId		: uint		= firstChildId + numChildren;

				localToWorld._hasChanged = false;

				for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
				{
					var childTransform		: Matrix4x4		= _transforms[childId];
					var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
					var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged;

					if (childIsDirty)
					{
						var child	: ISceneNode	= _idToNode[childId];

						childLocalToWorld
							.copyFrom(childTransform)
							.append(localToWorld);

						childTransform._hasChanged = false;

						child.localToWorldTransformChanged.execute(child, childLocalToWorld);
					}
				}
			}
		}

		private function updateAncestorsAndSelfLocalToWorld(nodeId : int) : void
		{
			var dirtyRoot : int = nodeId;

			while (nodeId >= 0)
			{
				if ((_transforms[nodeId] as Matrix4x4)._hasChanged)
					dirtyRoot = nodeId;

				nodeId = _parentId[nodeId];
			}

			if (dirtyRoot >= 0)
				updateLocalToWorld(dirtyRoot);
		}

		private function targetAddedHandler(ctrl	: TransformController,
											target	: ISceneNode) : void
		{
			if (_target)
				throw new Error('The TransformController cannot have more than one target.');

			_target = target;
			_invalidList = true;

			if (target is Scene)
			{
				(target as Scene).renderingBegin.add(renderingBeginHandler);
				return;
			}

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.add(descendantAddedHandler);
				targetGroup.descendantRemoved.add(descendantRemovedHandler);
			}

			target.added.add(addedHandler);
		}

		private function targetRemovedHandler(ctrl		: TransformController,
											  target	: ISceneNode) : void
		{
			target.added.remove(addedHandler);

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.remove(descendantAddedHandler);
				targetGroup.descendantRemoved.remove(descendantRemovedHandler);
			}

			_invalidList = false;
			_target = null;

			_nodeToId = null;
			_transforms = null;
			_localToWorldTransforms = null;
			_numChildren = null;
			_idToNode = null;
			_parentId = null;
		}

		private function addedHandler(target : ISceneNode, ancestor : Group) : void
		{
			// the controller will remove itself from the node when it's not its own root anymore
			// but it will watch for the 'removed' signal to add itself back if the node becomes
			// its own root again
			_target.removed.add(removedHandler);
			_target.removeController(this);
		}

		private function removedHandler(target : ISceneNode, ancestor : Group) : void
		{
			if (target.root == target)
			{
				target.removed.remove(removedHandler);
				target.addController(this);
			}
		}

		private function descendantAddedHandler(root		: Group,
												descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function descendantRemovedHandler(root			: Group,
												  descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function updateTransformsList() : void
		{
			var root 	: ISceneNode 			= _target.root;
			var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
			var nodeId 	: uint 					= 0;

			_nodeToId = new Dictionary(true);
			_transforms = new <Matrix4x4>[];
			_localToWorldTransforms = new <Matrix4x4>[];
			_numChildren = new <uint>[];
			_firstChildId = new <uint>[];
			_idToNode = new <ISceneNode>[];
			_parentId = new <int>[-1];

			while (nodes.length)
			{
				var node 	: ISceneNode 	= nodes.shift();
				var group 	: Group 		= node as Group;

				_nodeToId[node] = nodeId;
				_idToNode[nodeId] = node;
				_transforms[nodeId] = node.transform;
				_localToWorldTransforms[nodeId] = new Matrix4x4().lock();

				if (group)
				{
					var numChildren 	: uint = group.numChildren;
					var firstChildId 	: uint = nodeId + nodes.length + 1;

					_numChildren[nodeId] = numChildren;
					_firstChildId[nodeId] = firstChildId;
					for (var childId : uint = 0; childId < numChildren; ++childId)
					{
						_parentId[uint(firstChildId + childId)] = nodeId;
						nodes.push(group.getChildAt(childId));
					}
				}
				else
				{
					_numChildren[nodeId] = 0;
					_firstChildId[nodeId] = 0;
				}

				++nodeId;
			}

			_invalidList = false;
		}

		public function getLocalToWorldTransform(node 			: ISceneNode,
												 forceUpdate 	: Boolean = false) : Matrix4x4
		{
			if (_invalidList || _nodeToId[node] == undefined)
				updateTransformsList();

			var nodeId : uint = _nodeToId[node];

			if (forceUpdate)
				updateAncestorsAndSelfLocalToWorld(nodeId);

			return _localToWorldTransforms[nodeId];
		}
	}
}

Fix update of root localToWorldTransform.


package com.threerings.io.streamers {

import com.threerings.util.ClassUtil;

import com.threerings.io.ArrayMask;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
import com.threerings.io.Translations;
import com.threerings.io.TypedArray;

/**
 * A Streamer for Array objects.
 */
public class ArrayStreamer extends Streamer
{
    public function ArrayStreamer (jname :String = "[Ljava.lang.Object;")
    {
        super(TypedArray, jname);

        var secondChar :String = jname.charAt(1);
        if (secondChar === "[") {
            // if we're a multi-dimensional array then we need a delegate
            _delegate = Streamer.getStreamerByJavaName(jname.substring(1));
            _isFinal = true; // it just is

        } else if (secondChar === "L") {
            // form is "[L<class>;"
            var baseClass :String = jname.substring(2, jname.length - 1);
            baseClass = Translations.getFromServer(baseClass);
            _elementType = ClassUtil.getClassByName(baseClass);
            _isFinal = ClassUtil.isFinal(_elementType);

        } else if (secondChar === "I") {
            _elementType = int;

        } else if (secondChar === "Z") {
            _elementType = Boolean;

        } else {
            Log.getLog(this).warning("Other array types are " +
                "currently not handled yet [jname=" + jname + "].");
            throw new Error("Unimplemented bit");
        }
    }

    override public function isStreamerFor (obj :Object) :Boolean
    {
        if (obj is TypedArray) {
            // TypedArrays need the same element type
            return ((obj as TypedArray).getJavaType() === _jname);

        } else {
            // any other array is streamed as Object[]
            return (obj is Array) && (_jname === "[Ljava.lang.Object;");
        }
    }

    override public function isStreamerForClass (clazz :Class) :Boolean
    {
        if (clazz == TypedArray) {
            return false; // TODO: we're kinda fucked for finding a streamer
            // by class for TypedArrays here. The caller should be passing
            // the java name.

        } else {
            return ClassUtil.isAssignableAs(Array, clazz) &&
                (_jname === "[Ljava.lang.Object;");
        }
    }

    override public function createObject (ins :ObjectInputStream) :Object
    {
        var ta :TypedArray = new TypedArray(_jname);
        ta.length = ins.readInt();
        return ta;
    }

    override public function writeObject (obj :Object, out :ObjectOutputStream)
            :void
    {
        var arr :Array = (obj as Array);
        var ii :int;
        out.writeInt(arr.length);
        if (_elementType == int) {
            for (ii = 0; ii < arr.length; ii++) {
                out.writeInt(arr[ii] as int);
            }

        } else if (_isFinal) {
            var mask :ArrayMask = new ArrayMask(arr.length);
            for (ii = 0; ii < arr.length; ii++) {
                if (arr[ii] != null) {
                    mask.setBit(ii);
                }
            }
            mask.writeTo(out);
            // now write the populated elements
            for (ii = 0; ii < arr.length; ii++) {
                var element :Object = arr[ii];
                if (element != null) {
                    out.writeBareObjectImpl(element, _delegate);
                }
            }

        } else {
            for (ii = 0; ii < arr.length; ii++) {
                out.writeObject(arr[ii]);
            }
        }
    }

    override public function readObject (obj :Object, ins :ObjectInputStream)
            :void
    {
        var arr :Array = (obj as Array);
        var ii :int;
        if (_elementType == int) {
            for (ii = 0; ii < arr.length; ii++) {
                arr[ii] = ins.readInt();
            }

        } else if (_isFinal) {
            var mask :ArrayMask = new ArrayMask();
            mask.readFrom(ins);
            for (ii = 0; ii < length; ii++) {
                if (mask.isSet(ii)) {
                    var target :Object;
                    if (_delegate == null) {
                        target = new _elementType();
                    } else {
                        target = _delegate.createObject(ins);
                    }
                    ins.readBareObjectImpl(target, _delegate);
                    this[ii] = target;
                }
            }

        } else {
            for (ii = 0; ii < arr.length; ii++) {
                arr[ii] = ins.readObject();
            }
        }
    }

    /** A streamer for our elements. */
    protected var _delegate :Streamer;

    /** If this is the final dimension of the array, the element type. */
    protected var _elementType :Class;

    /** Whether we're final or not: true if we're not the final dimension
     * or if the element type is final. */
    protected var _isFinal :Boolean;
}
}

package com.threerings.io.streamers {

import com.threerings.util.ClassUtil;

import com.threerings.io.ArrayMask;
import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;
import com.threerings.io.Streamer;
import com.threerings.io.Translations;
import com.threerings.io.TypedArray;

/**
 * A Streamer for Array objects.
 */
public class ArrayStreamer extends Streamer
{
    public function ArrayStreamer (jname :String = "[Ljava.lang.Object;")
    {
        super(TypedArray, jname);

        var secondChar :String = jname.charAt(1);
        if (secondChar === "[") {
            // if we're a multi-dimensional array then we need a delegate
            _delegate = Streamer.getStreamerByJavaName(jname.substring(1));
            _isFinal = true; // it just is

        } else if (secondChar === "L") {
            // form is "[L<class>;"
            var baseClass :String = jname.substring(2, jname.length - 1);
            baseClass = Translations.getFromServer(baseClass);
            _elementType = ClassUtil.getClassByName(baseClass);
            _isFinal = ClassUtil.isFinal(_elementType);

        } else if (secondChar === "I") {
            _elementType = int;

        } else if (secondChar === "Z") {
            _elementType = Boolean;

        } else {
            Log.getLog(this).warning("Other array types are " +
                "currently not handled yet [jname=" + jname + "].");
            throw new Error("Unimplemented bit");
        }
    }

    override public function isStreamerFor (obj :Object) :Boolean
    {
        if (obj is TypedArray) {
            // TypedArrays need the same element type
            return ((obj as TypedArray).getJavaType() === _jname);

        } else {
            // any other array is streamed as Object[]
            return (obj is Array) && (_jname === "[Ljava.lang.Object;");
        }
    }

    override public function isStreamerForClass (clazz :Class) :Boolean
    {
        if (clazz == TypedArray) {
            return false; // TODO: we're kinda fucked for finding a streamer
            // by class for TypedArrays here. The caller should be passing
            // the java name.

        } else {
            return ClassUtil.isAssignableAs(Array, clazz) &&
                (_jname === "[Ljava.lang.Object;");
        }
    }

    override public function createObject (ins :ObjectInputStream) :Object
    {
        var ta :TypedArray = new TypedArray(_jname);
        ta.length = ins.readInt();
        return ta;
    }

    override public function writeObject (obj :Object, out :ObjectOutputStream)
            :void
    {
        var arr :Array = (obj as Array);
        var ii :int;
        out.writeInt(arr.length);
        if (_elementType == int) {
            for (ii = 0; ii < arr.length; ii++) {
                out.writeInt(arr[ii] as int);
            }

        } else if (_elementType == Boolean) {
            for (ii = 0; ii < arr.length; ii++) {
                out.writeBoolean(arr[ii] as Boolean);
            }

        } else if (_isFinal) {
            var mask :ArrayMask = new ArrayMask(arr.length);
            for (ii = 0; ii < arr.length; ii++) {
                if (arr[ii] != null) {
                    mask.setBit(ii);
                }
            }
            mask.writeTo(out);
            // now write the populated elements
            for (ii = 0; ii < arr.length; ii++) {
                var element :Object = arr[ii];
                if (element != null) {
                    out.writeBareObjectImpl(element, _delegate);
                }
            }

        } else {
            for (ii = 0; ii < arr.length; ii++) {
                out.writeObject(arr[ii]);
            }
        }
    }

    override public function readObject (obj :Object, ins :ObjectInputStream)
            :void
    {
        var arr :Array = (obj as Array);
        var ii :int;
        if (_elementType == int) {
            for (ii = 0; ii < arr.length; ii++) {
                arr[ii] = ins.readInt();
            }

        } else if (_elementType == Boolean) {
            for (ii = 0; ii < arr.length; ii++) {
                arr[ii] = ins.readBoolean();
            }

        } else if (_isFinal) {
            var mask :ArrayMask = new ArrayMask();
            mask.readFrom(ins);
            for (ii = 0; ii < length; ii++) {
                if (mask.isSet(ii)) {
                    var target :Object;
                    if (_delegate == null) {
                        target = new _elementType();
                    } else {
                        target = _delegate.createObject(ins);
                    }
                    ins.readBareObjectImpl(target, _delegate);
                    this[ii] = target;
                }
            }

        } else {
            for (ii = 0; ii < arr.length; ii++) {
                arr[ii] = ins.readObject();
            }
        }
    }

    /** A streamer for our elements. */
    protected var _delegate :Streamer;

    /** If this is the final dimension of the array, the element type. */
    protected var _elementType :Class;

    /** Whether we're final or not: true if we're not the final dimension
     * or if the element type is final. */
    protected var _isFinal :Boolean;
}
}

Implement Boolean array streaming.

Implement Boolean array streaming.

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@4319 542714f4-19e9-0310-aa3c-eee0fc999fb1


//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.crowd.data {

import com.threerings.util.Byte;
import com.threerings.util.Name;

import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationCodes;

import com.threerings.crowd.chat.data.ChatCodes;

import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;

/**
 * The basic user object class for Crowd users. Bodies have a username, a
 * location and a status.
 */
public class BodyObject extends ClientObject
{
    // AUTO-GENERATED: FIELDS START
    /** The field name of the username field. */
    public static const USERNAME :String = "username";

    /** The field name of the location field. */
    public static const LOCATION :String = "location";

    /** The field name of the status field. */
    public static const STATUS :String = "status";

    /** The field name of the awayMessage field. */
    public static const AWAY_MESSAGE :String = "awayMessage";
    // AUTO-GENERATED: FIELDS END

    /**
     * The username associated with this body object. This should not be used
     * directly; in general {@link #getVisibleName} should be used unless you
     * specifically know that you want the username.
     */
    public var username :Name;

    /**
     * The oid of the place currently occupied by this body or -1 if they
     * currently occupy no place.
     */
    public var location :int = -1;

    /**
     * The user's current status ({@link OccupantInfo#ACTIVE}, etc.).
     */
    public var status :int;

    /**
     * If non-null, this contains a message to be auto-replied whenever
     * another user delivers a tell message to this user.
     */
    public var awayMessage :String;

    /**
     * Checks whether or not this user has access to the specified
     * feature. Currently used by the chat system to regulate access to
     * chat broadcasts but also forms the basis of an extensible
     * fine-grained permissions system.
     *
     * @return null if the user has access, a fully-qualified translatable
     * message string indicating the reason for denial of access (or just
     * {@link InvocationCodes#ACCESS_DENIED} if you don't want to be
     * specific).
     */
    public function checkAccess (feature :String, context :Object) :String
    {
        // our default access control policy; how quaint
        if (ChatCodes.BROADCAST_ACCESS == feature) {
            return getTokens().isAdmin() ? null : InvocationCodes.ACCESS_DENIED;
        } else if (ChatCodes.CHAT_ACCESS == feature) {
            return null;
        } else {
            return InvocationCodes.ACCESS_DENIED;
        }
    }

    /**
     * Returns this user's access control tokens.
     */
    public function getTokens () :TokenRing
    {
        return new TokenRing();
    }

    /**
     * Returns the name that should be displayed to other users and used for
     * the chat system. The default is to use {@link #username}.
     */
    public function getVisibleName () :Name
    {
        return username;
    }

//    // AUTO-GENERATED: METHODS START
//    /**
//     * Requests that the username field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setUsername (value :Name) :void
//    {
//        var ovalue :Name = this.username;
//        requestAttributeChange(
//            USERNAME, value, ovalue);
//        this.username = value;
//    }
//
//    /**
//     * Requests that the location field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setLocation (value :int) :void
//    {
//        var ovalue :int = this.location;
//        requestAttributeChange(
//            LOCATION, value, ovalue);
//        this.location = value;
//    }
//
//    /**
//     * Requests that the status field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setStatus (value :int) :void
//    {
//        var ovalue :int = this.status;
//        requestAttributeChange(
//            STATUS, Byte.valueOf(value), Byte.valueOf(ovalue));
//        this.status = value;
//    }
//
//    /**
//     * Requests that the awayMessage field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setAwayMessage (value :String) :void
//    {
//        var ovalue :String = this.awayMessage;
//        requestAttributeChange(
//            AWAY_MESSAGE, value, ovalue);
//        this.awayMessage = value;
//    }
//    // AUTO-GENERATED: METHODS END
//
//    override public function writeObject (out :ObjectOutputStream) :void
//    {
//        super.writeObject(out);
//
//        out.writeObject(username);
//        out.writeInt(location);
//        out.writeByte(status);
//        out.writeField(awayMessage);
//    }

    override public function readObject (ins :ObjectInputStream) :void
    {
        super.readObject(ins);

        username = (ins.readObject() as Name);
        location = ins.readInt();
        status = ins.readByte();
        awayMessage = (ins.readField(String) as String);
    }
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.crowd.data {

import com.threerings.util.Byte;
import com.threerings.util.Name;

import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationCodes;

import com.threerings.crowd.chat.data.ChatCodes;

import com.threerings.io.ObjectInputStream;
import com.threerings.io.ObjectOutputStream;

/**
 * The basic user object class for Crowd users. Bodies have a username, a
 * location and a status.
 */
public class BodyObject extends ClientObject
{
    // AUTO-GENERATED: FIELDS START
    /** The field name of the username field. */
    public static const USERNAME :String = "username";

    /** The field name of the location field. */
    public static const LOCATION :String = "location";

    /** The field name of the status field. */
    public static const STATUS :String = "status";

    /** The field name of the awayMessage field. */
    public static const AWAY_MESSAGE :String = "awayMessage";
    // AUTO-GENERATED: FIELDS END

    /**
     * The username associated with this body object. This should not be used
     * directly; in general {@link #getVisibleName} should be used unless you
     * specifically know that you want the username.
     */
    public var username :Name;

    /**
     * The oid of the place currently occupied by this body or -1 if they
     * currently occupy no place.
     */
    public var location :int = -1;

    /**
     * The user's current status ({@link OccupantInfo#ACTIVE}, etc.).
     */
    public var status :int;

    /**
     * If non-null, this contains a message to be auto-replied whenever
     * another user delivers a tell message to this user.
     */
    public var awayMessage :String;

    /**
     * Checks whether or not this user has access to the specified
     * feature. Currently used by the chat system to regulate access to
     * chat broadcasts but also forms the basis of an extensible
     * fine-grained permissions system.
     *
     * @return null if the user has access, a fully-qualified translatable
     * message string indicating the reason for denial of access (or just
     * {@link InvocationCodes#ACCESS_DENIED} if you don't want to be
     * specific).
     */
    public function checkAccess (feature :String, context :Object) :String
    {
        // our default access control policy; how quaint
        if (ChatCodes.BROADCAST_ACCESS == feature) {
            return getTokens().isAdmin() ? null : InvocationCodes.ACCESS_DENIED;
        } else if (ChatCodes.CHAT_ACCESS == feature) {
            return null;
        } else {
            return InvocationCodes.ACCESS_DENIED;
        }
    }

    /**
     * Returns this user's access control tokens.
     */
    public function getTokens () :TokenRing
    {
        return new TokenRing();
    }

    /**
     * Returns the name that should be displayed to other users and used for
     * the chat system. The default is to use {@link #username}.
     */
    public function getVisibleName () :Name
    {
        return username;
    }

//    // AUTO-GENERATED: METHODS START
//    /**
//     * Requests that the username field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setUsername (value :Name) :void
//    {
//        var ovalue :Name = this.username;
//        requestAttributeChange(
//            USERNAME, value, ovalue);
//        this.username = value;
//    }
//
//    /**
//     * Requests that the location field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setLocation (value :int) :void
//    {
//        var ovalue :int = this.location;
//        requestAttributeChange(
//            LOCATION, value, ovalue);
//        this.location = value;
//    }
//
//    /**
//     * Requests that the status field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setStatus (value :int) :void
//    {
//        var ovalue :int = this.status;
//        requestAttributeChange(
//            STATUS, Byte.valueOf(value), Byte.valueOf(ovalue));
//        this.status = value;
//    }
//
//    /**
//     * Requests that the awayMessage field be set to the
//     * specified value. The local value will be updated immediately and an
//     * event will be propagated through the system to notify all listeners
//     * that the attribute did change. Proxied copies of this object (on
//     * clients) will apply the value change when they received the
//     * attribute changed notification.
//     */
//    public function setAwayMessage (value :String) :void
//    {
//        var ovalue :String = this.awayMessage;
//        requestAttributeChange(
//            AWAY_MESSAGE, value, ovalue);
//        this.awayMessage = value;
//    }
//    // AUTO-GENERATED: METHODS END
//
//    override public function writeObject (out :ObjectOutputStream) :void
//    {
//        super.writeObject(out);
//
//        out.writeObject(username);
//        out.writeInt(location);
//        out.writeByte(status);
//        out.writeField(awayMessage);
//    }

    override public function who () :String
    {
        var who :String = username.toString() + " (" + getOid();
        if (status != OccupantInfo.ACTIVE) {
            who += (" " + OccupantInfo.X_STATUS[status]);
        }
        who += ")";
        return who;
    }

    override public function readObject (ins :ObjectInputStream) :void
    {
        super.readObject(ins);

        username = (ins.readObject() as Name);
        location = ins.readInt();
        status = ins.readByte();
        awayMessage = (ins.readField(String) as String);
    }
}
}

Implement bodyObject's proper who().

Implement bodyObject's proper who().

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@4627 542714f4-19e9-0310-aa3c-eee0fc999fb1


/**
 * Created with IntelliJ IDEA.
 * User: promethe
 * Date: 12/04/13
 * Time: 20:03
 * To change this template use File | Settings | File Templates.
 */
package aerys.minko.scene.controller.animation
{
	import flash.utils.Dictionary;

	import aerys.minko.ns.minko_animation;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.IRebindableController;

	public class MasterAnimationController extends AbstractController implements IAnimationController, IRebindableController
	{
		use namespace minko_animation;
		minko_animation var _animations : Vector.<IAnimationController>;
		private var _isPlaying  		: Boolean;

		private var _labelNames : Vector.<String>;
		private var _labelTimes : Vector.<Number>;

		public function get numLabels() : uint
		{
			return _labelNames.length;
		}

		public function get isPlaying() : Boolean
		{
			return _isPlaying;
		}

		public function MasterAnimationController(animations : Vector.<IAnimationController>)
		{
			super();

			_animations = animations.concat();

			_labelNames = new <String>[];
			_labelTimes = new <Number>[];
		}

		public function addLabel(name : String, time : Number) : IAnimationController
		{
			if (_labelNames.indexOf(name) >= 0)
				throw new Error('A label with the same name already exists.');

			_labelNames.push(name);
			_labelTimes.push(time);

			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].addLabel(name,  time);

			return this;
		}

		public function removeLabel(name : String) : IAnimationController
		{
			var index : int = _labelNames.indexOf(name);

			if (index < 0)
				throw new Error('The time label named \'' + name + '\' does not exist.');

			var numLabels : uint = _labelNames.length - 1;

			_labelNames[index] = _labelNames[numLabels];
			_labelNames.length = numLabels;

			_labelTimes[index] = _labelTimes[numLabels];
			_labelTimes.length = numLabels;

			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].removeLabel(name);

			return this;
		}

		public function seek(time : Object) : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].seek(time);

			return this;
		}

		public function play() : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			_isPlaying = true;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].play();

			return this;
		}

		public function stop() : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			_isPlaying = false;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].stop();

			return this;
		}

		public function setPlaybackWindow(beginTime : Object = null, endTime : Object = null) : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].setPlaybackWindow(beginTime, endTime);

			return this;
		}

		public function resetPlaybackWindow() : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].resetPlaybackWindow();

			return this;
		}

		public function getLabelName(index : uint) : String
		{
			return _labelNames[index];
		}

		public function getLabelTime(index : uint) : Number
		{
			return _labelTimes[index];
		}

		public function getLabelTimeByName(name : String) : Number
		{
			var index : int = _labelNames.indexOf(name);

			if (index < 0)
				throw new Error('The time label named \'' + name + '\' does not exist.');

			return _labelTimes[index];
		}

		override public function clone():AbstractController
		{
			var newController : MasterAnimationController = new MasterAnimationController(_animations);
			newController._labelNames = _labelNames;
			newController._labelTimes = _labelTimes;

			return newController;
		}

		public function rebindDependencies(nodeMap : Dictionary, controllerMap : Dictionary) : void
		{
			var newAnimations : Vector.<IAnimationController> = new Vector.<IAnimationController>();

			for(var i : int = 0; i < _animations.length; ++i)
			{
				var newController : IAnimationController = controllerMap[_animations[i]] as IAnimationController;
				if (newController)
					newAnimations.push(newController);
			}

			_animations = newAnimations;
		}

		public function changeLabel(oldName : String, newName : String) : IAnimationController
		{
			var index : int = _labelNames.indexOf(oldName);

			if (index < 0)
				throw new Error('The time label named \'' + oldName + '\' does not exist.');

			_labelNames[index] = newName;

			return this;
		}

		public function setTimeForLabel(name : String, newTime : Number) : IAnimationController
		{
			var index : int = _labelNames.indexOf(name);

			if (index < 0)
				throw new Error('The time label named \'' + name + '\' does not exist.');

			_labelTimes[index] = newTime;

			return this;
		}
	}
}

/**
 * Created with IntelliJ IDEA.
 * User: promethe
 * Date: 12/04/13
 * Time: 20:03
 * To change this template use File | Settings | File Templates.
 */
package aerys.minko.scene.controller.animation
{
	import flash.utils.Dictionary;

	import aerys.minko.ns.minko_animation;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.IRebindableController;

	public class MasterAnimationController extends AbstractController implements IAnimationController, IRebindableController
	{
		use namespace minko_animation;
		minko_animation var _animations : Vector.<IAnimationController>;
		private var _isPlaying  		: Boolean;

		private var _labelNames : Vector.<String>;
		private var _labelTimes : Vector.<Number>;

		public function get numLabels() : uint
		{
			return _labelNames.length;
		}

		public function get isPlaying() : Boolean
		{
			return _isPlaying;
		}

		public function MasterAnimationController(animations : Vector.<IAnimationController>)
		{
			super();

			_animations = animations.concat();

			_labelNames = new <String>[];
			_labelTimes = new <Number>[];
		}

		public function addLabel(name : String, time : Number) : IAnimationController
		{
			if (_labelNames.indexOf(name) >= 0)
				throw new Error('A label with the same name already exists.');

			_labelNames.push(name);
			_labelTimes.push(time);

			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].addLabel(name,  time);

			return this;
		}

		public function removeLabel(name : String) : IAnimationController
		{
			var index : int = _labelNames.indexOf(name);

			if (index < 0)
				throw new Error('The time label named \'' + name + '\' does not exist.');

			var numLabels : uint = _labelNames.length - 1;

			_labelNames[index] = _labelNames[numLabels];
			_labelNames.length = numLabels;

			_labelTimes[index] = _labelTimes[numLabels];
			_labelTimes.length = numLabels;

			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].removeLabel(name);

			return this;
		}

		public function seek(time : Object) : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].seek(time);

			return this;
		}

		public function play() : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			_isPlaying = true;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].play();

			return this;
		}

		public function stop() : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			_isPlaying = false;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].stop();

			return this;
		}

		public function setPlaybackWindow(beginTime : Object = null, endTime : Object = null) : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].setPlaybackWindow(beginTime, endTime);

			return this;
		}

		public function resetPlaybackWindow() : IAnimationController
		{
			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].resetPlaybackWindow();

			return this;
		}

		public function getLabelName(index : uint) : String
		{
			return _labelNames[index];
		}

		public function getLabelTime(index : uint) : Number
		{
			return _labelTimes[index];
		}

		public function getLabelTimeByName(name : String) : Number
		{
			var index : int = _labelNames.indexOf(name);

			if (index < 0)
				throw new Error('The time label named \'' + name + '\' does not exist.');

			return _labelTimes[index];
		}

		override public function clone():AbstractController
		{
			var newController : MasterAnimationController = new MasterAnimationController(_animations);
			newController._labelNames = _labelNames;
			newController._labelTimes = _labelTimes;

			return newController;
		}

		public function rebindDependencies(nodeMap : Dictionary, controllerMap : Dictionary) : void
		{
			var newAnimations : Vector.<IAnimationController> = new Vector.<IAnimationController>();

			for(var i : int = 0; i < _animations.length; ++i)
			{
				var newController : IAnimationController = controllerMap[_animations[i]] as IAnimationController;
				if (newController)
					newAnimations.push(newController);
			}

			_animations = newAnimations;
		}

		public function changeLabel(oldName : String, newName : String) : IAnimationController
		{
			var index : int = _labelNames.indexOf(oldName);

			if (index < 0)
				throw new Error('The time label named \'' + oldName + '\' does not exist.');

			_labelNames[index] = newName;

			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].changeLabel(oldName, newName);

			return this;
		}

		public function setTimeForLabel(name : String, newTime : Number) : IAnimationController
		{
			var index : int = _labelNames.indexOf(name);

			if (index < 0)
				throw new Error('The time label named \'' + name + '\' does not exist.');

			_labelTimes[index] = newTime;

			var numAnimations : uint = _animations.length;

			for (var animationId : uint = 0; animationId < numAnimations; ++animationId)
				_animations[animationId].setTimeForLabel(name, newTime);

			return this;
		}
	}
}

Fix label modification on masterAnimationContoller


package aerys.minko.scene.node.texture
{
	import aerys.minko.render.effect.basic.BasicStyle;
	import aerys.minko.render.ressource.TextureRessource;
	import aerys.minko.scene.action.texture.TextureAction;
	import aerys.minko.scene.node.AbstractScene;

	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.geom.Matrix;

	/**
	 * The BitmapTexture class enables the use of BitmapData objects as
	 * textures.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class BitmapTexture extends AbstractScene implements ITexture
	{
		private var _version	: uint				= 0;

		private var _data		: BitmapData		= null;
		private var _mipmapping	: Boolean			= false;

		private var _styleProp	: int				= -1;

		private var _matrix		: Matrix			= new Matrix();

		private var _ressource	: TextureRessource	= new TextureRessource();

		public function get version() : uint
		{
			return _version;
		}

		public function get styleProperty() : int
		{
			return _styleProp;
		}

		public function set styleProperty(value : int) : void
		{
			_styleProp = value;
		}

		public function get ressource() : TextureRessource
		{
			return _ressource;
		}

		protected function get bitmapData() : BitmapData
		{
			return _data;
		}

		public function updateFromBitmapData(value 	: BitmapData,
											 smooth	: Boolean	= true) : void
		{
			var size 	: int = 1;
			var w 		: int = value.width;
			var h 		: int = value.height;

			while (size < w || size < h)
				size <<= 1;

			if (!_data || _data.width != size || _data.height != size)
				_data = new BitmapData(size, size, value.transparent, 0);

			if (size != w || size != h)
			{
				_matrix.identity();
				_matrix.scale(size / value.width, size / value.height);
				_data.draw(value, _matrix, null, null, null, smooth);
			}
			else
			{
				_data.draw(value, null, null, null, null, smooth);
			}

			++_version;

			_ressource.setContentFromBitmapData(_data, _mipmapping);
		}

		public function BitmapTexture(bitmapData 	: BitmapData 	= null,
									  mipmapping	: Boolean		= true)
		{
			_mipmapping	= mipmapping;

			if (bitmapData)
				updateFromBitmapData(bitmapData);

			_styleProp = BasicStyle.DIFFUSE;

			actions[0] = TextureAction.textureAction;
		}

		public static function fromDisplayObject(source : DisplayObject,
												 size 	: int 		= 0,
												 smooth : Boolean 	= false) : BitmapTexture
		{
			var matrix : Matrix = null;
			var bmp : BitmapData = new BitmapData(size || source.width,
												  size || source.height,
												  true,
												  0);

			if (size)
			{
				matrix = new Matrix();
				matrix.scale(source.width / size, source.height / size);
			}

			bmp.draw(source, matrix, null, null, null, smooth);

			return new BitmapTexture(bmp);
		}
	}
}

package aerys.minko.scene.node.texture
{
	import aerys.minko.render.effect.basic.BasicStyle;
	import aerys.minko.render.ressource.TextureRessource;
	import aerys.minko.scene.action.texture.TextureAction;
	import aerys.minko.scene.node.AbstractScene;

	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.geom.Matrix;

	/**
	 * The BitmapTexture class enables the use of BitmapData objects as
	 * textures.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class BitmapTexture extends AbstractScene implements ITexture
	{
		private var _version	: uint				= 0;

		private var _data		: BitmapData		= null;
		private var _mipmapping	: Boolean			= false;

		private var _styleProp	: int				= -1;

		private var _matrix		: Matrix			= new Matrix();

		private var _ressource	: TextureRessource	= new TextureRessource();

		public function get version() : uint
		{
			return _version;
		}

		public function get styleProperty() : int
		{
			return _styleProp;
		}

		public function set styleProperty(value : int) : void
		{
			_styleProp = value;
		}

		public function get ressource() : TextureRessource
		{
			return _ressource;
		}

		protected function get bitmapData() : BitmapData
		{
			return _data;
		}

		public function updateFromBitmapData(value 	: BitmapData,
											 smooth	: Boolean	= true) : void
		{
			var size 	: int = 1;
			var w 		: int = value.width;
			var h 		: int = value.height;

			while (size < w || size < h)
				size <<= 1;

			if (!_data || _data.width != size || _data.height != size)
				_data = new BitmapData(size, size, value.transparent, 0);

			if (size != w || size != h)
			{
				_matrix.identity();
				_matrix.scale(size / value.width, size / value.height);
				_data.draw(value, _matrix, null, null, null, smooth);
			}
			else
			{
				_data.draw(value, null, null, null, null, smooth);
			}

			++_version;

			_ressource.setContentFromBitmapData(_data, _mipmapping);
		}

		public function BitmapTexture(bitmapData 	: BitmapData 	= null,
									  mipmapping	: Boolean		= true,
									  styleProp		: int			= -1)
		{
			_mipmapping	= mipmapping;

			if (bitmapData)
				updateFromBitmapData(bitmapData);

			_styleProp = styleProp != -1 ? styleProp : BasicStyle.DIFFUSE;

			actions[0] = TextureAction.textureAction;
		}

		public static function fromDisplayObject(source : DisplayObject,
												 size 	: int 		= 0,
												 smooth : Boolean 	= false) : BitmapTexture
		{
			var matrix : Matrix = null;
			var bmp : BitmapData = new BitmapData(size || source.width,
												  size || source.height,
												  true,
												  0);

			if (size)
			{
				matrix = new Matrix();
				matrix.scale(source.width / size, source.height / size);
			}

			bmp.draw(source, matrix, null, null, null, smooth);

			return new BitmapTexture(bmp);
		}
	}
}

Add optional argument to BitmapTexture to set texture type