Share 'Invalidation (I10N)' on Delicious Share 'Invalidation (I10N)' on Facebook Share 'Invalidation (I10N)' on Google Bookmarks Share 'Invalidation (I10N)' on Twitter

  1. I10N workflow
  2. Principle of operation
    1. System architecture
    2. System set up
    3. Validation cycle
    4. Validation phases
    5. Repeated invalidations
    6. Invalid properties
    7. Stage or not stage
    8. Nest level
    9. validateNow()
    10. Removing objects
  3. Setting up I10N
  4. Adding validation phases
  5. Setting up the I10N adapter
    1. onValidate()
    2. Nest level
    3. onAddedToStage(), onRemovedFromStage()
  6. Setting up a component
    1. Implementing the validation method
    2. Register the component with I10N
    3. Invalidating the component
    4. Immediately validating the component
    5. Property invalidation
    6. Full invalidation
  7. Example application
    1. I10N service configuration
    2. The main application
    3. The component
    4. The adapter
    5. The example SWF
  8. Leave a Comment

Invalidation (I10N)

I10N is an abstract implementation of a phase based component validation manager. If you know Flex, I10N is a configurable alternative to the Flex LayoutManager.

I10N enables arbitrary display objects to execute the expensive visual updates and calculations only once while setting of properties is not restricted and may occur repeatedly. The idea behind is, instead of having a component updated instantly, invalidation inserts a small frame of time between the setting of a property and the visual aligment. The component is marked invalid, and finally, after that frame of time, validated.

I10N workflow

Click the chart to enlarge.

Principle of operation

System architecture

With I10N there are three participants. The component, the I10N adapter and the I10N service. The developer creates the component and configures an adapter by subclassing the I10NAdapter class. Both, component and adapter must be registered with I10N. The I10N system maintains an internal mapping of registered display objects to their adapters. I10N will call internal methods on the adapter (validation callbacks). The adapter is supposed to delegate those calls to the component, so the components must provide an interface that supports the validation callback delegation.

System set up

You as the component developer need to create and setup I10N and establish the communication between component and adapter.

  • Create instance of I10N and add validation phases
  • Subclass I10NAdapter
  • Override the validation hook onValidate()
  • Create a (public or internal) validation method in the component
  • Call this method from the overridden validation hook
  • Register component and adapter with I10N

Validation cycle

The validation cycle of I10N begins with the first component invalidated. The service starts an internal timer with a delay of 0 (zero). When the timer event fires, the component invalidates the stage and registers for the stage’s RENDER event. If this event is dispatched, all validation phases are processed in their specified order. The cycle finishes when all phases are empty again. The cycle runs again with the next component invalidated.

Validation phases

I10N supports a configurable number of validation phases. A particular phase has a distinct queuing order (top-down, bottom-up) and a navigation instruction. Usually, phases are passed one by one in the order they were added to I10N. However, during a validation cycle items may be added to previous phases. In such a case the navigation instruction applies. Being in the current phase, the system tests whether items have been added to previous phases:

  • after each item validated (PHASE_LOOPBACK_AFTER_ITEM)
  • after the entire phase is finished (I10N.PHASE_LOOPBACK_AFTER_PHASE)
  • never, except we finished the last phase (PHASE_LOOPBACK_NONE)

Repeated invalidations

Each phase hosts a queue containing all components scheduled for being considerend during the pass of the phase. You may invalidate a component multiple times for the same phase. However, the queue will not add multiple instances of your component and return the component only once during the validation cycle.

Invalid properties

In many cases in our component validation method we want to know, what property exactly has changed. We do not need to redraw the background if only the text color has been updated for a button component. For cases like this, it is possible to pass a property to the invalidate() method of the I10N adapter. At the time the valdidation is requested, the component may test the adapter if a property has been invalidated or not.

Stage or not stage

Validations for components not added to the display list are not performed. Instead, I10N listens to the Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE for each component registered. Once an invalid component has been added (or readded), it is automatically re-scheduled to all of its invalid phases. Vice versa, a component removed from the stage will also be (temporarily) removed from all validation phases.

The stage handling makes it possible to invalidate a component right in the constructor of the component and schedule its initial drawing to a later time after it is added to the display list.

Nest level

To establish the queuing order in the validation phases, I10N monitors the display list position of each component added and maintains its nest level. The nest level of a component is available through the adapter’s nestLevel property. The nest level of a component not in the display list is -1.

Additionally, I10N notifies the adapter at any time its component has been added or removed from the stage. The adapter’s onAddedToStage and onRemovedFromStage callbacks are triggered.

validateNow()

validateNow() is a method to instantly validate either all invalid objects entirely or a particular component. It’s recommended to use this method only in rare situations.

There are a little number of considerations using validateNow():

  • You cannot call this method during a validation run (in validation methods). You are only allowed to call this method in main application code.
  • When called for a component (I10NAdapter::validateNow()), the subject component and all of its children are validated.
  • When called for a component, further invalidations for the subject component and its children are processed instantly. Further invalidations for other components are scheduled to next usual validation cycle.

Removing objects

Before you dispose your component you should call the cleanUp() method on I10N. The component gets unregistered from the internal map and I10N stops listening to the ADDED or REMOVED events.

Setting up I10N

You create an instance of I10N, add a number of validation phases and commit the setup by calling the start() method. There can be multiple instances of I10N but it is recommended to only create a sole instance. It is up to you to manage the instance using a global, a singleton, monostate or pooling pattern.

SetupI10N.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package lifecycle.i10n {
  import flash.display.Sprite;
  import org.as3commons.ui.lifecycle.i10n.I10N;

  public class SetupI10N extends Sprite {
    public function SetupI10N() {
      var i10n : I10N = new I10N();
      i10n.addPhase("phase1Name", I10N.PHASE_ORDER_TOP_DOWN);
      i10n.addPhase("phase2Name", I10N.PHASE_ORDER_BOTTOM_UP);
      // add more phases
      i10n.start();
    }
  }
}

Adding validation phases

You must configure at least on validation phase. You use the addPhase() method.

public function addPhase(phaseName : String, order : String, loopback : String = "loopback_after_item") : void;

The method accepts three parameters:

Parameter Description
phaseName Name of the phase to later refer to in all validation methods
order Order of items added to that phase. I10N.PHASE_ORDER_TOP_DOWN or I10N.PHASE_ORDER_BOTTOM_UP
loopback Condition when the system should rewind in the case items are added to previous phase during a validation run. Default is I10N.PHASE_LOOPBACK_AFTER_ITEM. The first phase is always hard set to I10N.PHASE_LOOPBACK_NONE.

Setting up the I10N adapter

I10N is designed to work with independent components or display objects. You don’t need to extend a base class or implement a specific interface. To anyhow let I10N communicate with your component you need to create and register a component adapter. This adapter must subclass the I10NApapter class and is therefore known by I10N. On the other hand, the adapter knows the component interface and can delegate the I10N callbacks to your component.

onValidate()

For a running adapter you need to implement at least the validation hook:

protected function onValidate(phaseName : String) : void;

validate(phaseName) is supposed to call an appropriate validation method on your component. During the component validation you can always test for specific invalid properties. After the component has been validated for a specific phase, all invalid properties are removed. Being in the second phase, you will get a false when testing for invalid properties of the first phase.

public function isInvalid(phaseName : String = null, property : String = null) : Boolean;
SimpleAdapter.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package lifecycle.i10n {
  import org.as3commons.ui.lifecycle.i10n.I10NAdapter;

  public class SimpleAdapter extends I10NAdapter {
    override protected function onValidate(phaseName : String) : void {
      Dummy(displayObject).render();
    }
  }
}

import flash.display.Sprite;
internal class Dummy extends Sprite {
  public function render() : void {
    // draw something
  }
}

Nest level

The I10NApapter consists of a nest level property which returns the display list position of the component. You are safe to rely on that property also for your component.

onAddedToStage(), onRemovedFromStage()

Whenever the component is added or removed from the stage, its adapter is notified by I10N. Since I10N monitors the add and remove events of its registered components anyway, its a convenience to offer both hooks that may be delegated to the component.

protected function onAddedToStage() : void;

protected function onRemovedFromStage() : void

Setting up a component

In order to call methods on the adapter, the particular component needs to receive a reference to the adapter. This can be accomplished in several ways. Usually you let the component create the adapter in its constructor.

Implementing the validation method

The I10N component adapter calls an appropriate validation method on the component within its validate() callback. In order to do so, such a method must exist. Its entirely free how to implement that method. Using a custom namespace is possible as well as passing a random number of arguments. The only thing is that that the adapter may reach the method.

function myValidationMethod() : void {
  // do something
}

Register the component with I10N

Having the adapter and the component, we can register both. It’s up to you how to access the particular I10N instance. A simple singleton might do the job.

SimpleRegistration.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package lifecycle.i10n {
  import flash.display.Sprite;
  import org.as3commons.ui.lifecycle.i10n.I10N;

  public class SimpleRegistration extends Sprite {
    public function SimpleRegistration() {
      var i10n : I10N = new I10N();
      i10n.addPhase("first", I10N.PHASE_ORDER_TOP_DOWN);
      i10n.start();
     
      i10n.registerDisplayObject(new Dummy(), new SimpleAdapter());
    }
  }
}

import flash.display.Sprite;
internal class Dummy extends Sprite {
  public function render() : void {
    // draw something
  }
}

After the registration is done, your component can operate on its adapter and receive notifications from I10N.

Invalidating the component

The last explanation point is how and when to trigger the invalidation process. You should place a call to the apdater’s invalidate() method on each place where UI related properties have been changed. This might be a color value for a background or the text of a label. Additionally you might put an initial invalidation call right into the constructor of your component to schedule an initial validation.

function MyComponent {
  _i10nAdapter.invalidate("phase1"); // invalidate everything
}

function set color(color : uint) : void {
  _color = color;
  _i10nAdapter.invalidate("phase1", "color");
}

function set text(text : String) : void {
  _text = text;
  _i10nAdapter.invalidate("phase1", "text");
}

function validate(phaseName : String) : void {
  // do something
}

Immediately validating the component

A call to _i10nAdapter.validateNow() will immediatly validate the component – if the component has been invalidated beforehand. This can be useful when necessary properties of the component are calculated during the validation and we need these properties right away.

_i10nAdapter.validateNow();

Property invalidation

In the majority of use cases a general and complete visual update won’t be desired. We need a possibility to update only parts of a component depending on the reason of invalidation. The I10NAdapter allows you to pass an optional property to its invalidate() method. Once the validation method of the component is called, the adapter can be tested if a property has been invalidated beforehand. This lets you filter out unneccessary calculations.

public function validate(phaseName) : void {
  if (phaseName == "commit") MyComponent(displayObject).commit();
  if (phaseName == "measure") MyComponent(displayObject).measure();
  if (phaseName == "render") MyComponent(displayObject).render();
}
function set color(color : uint) : void {
  _color = color;
  _i10nAdapter.invalidate("commit", "color");
}

function set text(text : String) : void {
  _text = text;
  _i10nAdapter.invalidate("commit", "text");
}

function commit() : void {
  if (_i10nAdapter.isInvalid("commit", "color")) {
    _i10nAdapter.invalidate("render", "background");
  }
  if (_i10nAdapter.isInvalid("commit", "text")) {
    _i10nAdapter.invalidate("render", "label");
  }
}

Full invalidation

If you call _i10nAdapter.invalidate() without specification of a particular invalid property. I10N assumes a request for a validation of all component parts and will later return true for a test of any property.

Example: Property invalidation

function test() : void {
  _i10nAdapter.invalidate("phase1", "test");
}

function validate() : void {
  trace (_i10nAdapter.isInvalidForAnyPhase(); // true
  trace (_i10nAdapter.isInvalid("phase1"); // true
  trace (_i10nAdapter.isInvalid("phase1", "test"); // true
  trace (_i10nAdapter.isInvalid("phase1", "test2"); // false
}

Example: Property invalidation overridden by a full invalidation

function test() : void {
  _i10nAdapter.invalidate("phase1", "test");
  _i10n.invalidate("phase1");
}

function validate(properties : ISet) : void {
  trace (_i10nAdapter.isInvalidForAnyPhase(); // true
  trace (_i10nAdapter.isInvalid("phase1"); // true
  trace (_i10nAdapter.isInvalid("phase1", "test"); // true
  trace (_i10nAdapter.isInvalid("phase1", "test2"); // true
}

Example application

The example application shows the set up of I10N, a component and its adapter.

I10N service configuration

We simply extend I10N, add a static instance variable (singleton pattern) and configure a single phase in a static start method.

I10NService.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package lifecycle.i10n.boxexample {
  import org.as3commons.ui.lifecycle.i10n.I10N;

  public class I10NService extends I10N {
    public static const PHASE_VALIDATE : String = "validate";
    private static var _instance : I10N;
   
    public static function start() : void {
      _instance = new I10N();
      _instance.addPhase(PHASE_VALIDATE, I10N.PHASE_ORDER_TOP_DOWN);
      _instance.start();
    }
   
    public static function get instance() : I10N {
      return _instance;
    }
  }
}

The main application

The main class starts our just created I10NService and adds a number of boxes. For demonstration purposed, the main application modifies different box properties several times.

BoxI10NExample.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package lifecycle.i10n.boxexample {
  import flash.display.Sprite;

  public class BoxI10NExample extends Sprite {
    public function BoxI10NExample() {
      I10NService.start();
     
      var box1 : Box = new Box("Box1");
      addChild(box1);

      var box2 : Box = new Box("Box2");
      box2.x = 120;
      box2.backgroundColor = 0x333333;
      box2.borderColor = 0x999999;
      box2.backgroundColor = 0x999999;
      box2.borderColor = 0x333333;
      addChild(box2);

      var box3 : Box = new Box("Box3");
      box3.x = 240;
      box3.backgroundColor = 0xCC0000;
      box3.borderColor = 0x550000;
      addChild(box3);
      box3.backgroundColor = 0x3366CC;
      box3.borderColor = 0x002244;

      var box4 : Box = new Box("Box4");
      box4.x = 360;
      addChild(box4);
      box4.backgroundColor = 0x3366CC;
      box4.borderColor = 0x002244;
      box4.backgroundColor = 0xCC6633;
      box4.borderColor = 0x442200;
    }
  }
}

The component

The box creates its I10NAdapter in the constructor and registers itself with I10N using the singleton method of our I10NService. Whenever a box property changes, the component calls the invalidate() method on the apdater.

Box.as
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package lifecycle.i10n.boxexample {
  import flash.display.Sprite;

  public class Box extends Sprite {
    private var _adapter : BoxAdapter;
    private var _backgroundColor : uint = 0xCCCCCC;
    private var _borderColor : uint = 0x999999;

    public function Box(boxName : String) {
      name = boxName;
     
      _adapter = new BoxAdapter();
      I10NService.instance.registerDisplayObject(this, _adapter);
      _adapter.invalidate(I10NService.PHASE_VALIDATE);
    }

    public function set backgroundColor(backgroundColor : uint) : void {
      _backgroundColor = backgroundColor;
      _adapter.invalidate(I10NService.PHASE_VALIDATE);
    }

    public function set borderColor(borderColor : uint) : void {
      _borderColor = borderColor;
      _adapter.invalidate(I10NService.PHASE_VALIDATE);
    }

    public function update() : void {
      trace ("UPDATE", name);
      with (graphics) {
        clear();
        lineStyle(1, _borderColor);
        beginFill(_backgroundColor);
        drawRect(0, 0, 100, 100);
      }
    }
  }
}

The adapter

The box adapter does nothing special but delegate the validate callback to the component’s render method.

BoxAdapter.as
1
2
3
4
5
6
7
8
9
package lifecycle.i10n.boxexample {
  import org.as3commons.ui.lifecycle.i10n.I10NAdapter;

  public class BoxAdapter extends I10NAdapter {
    override protected function onValidate(phaseName : String) : void {
      Box(displayObject).update();
    }
  }
}

The example SWF

BoxI10NExample.swf

As the console shows, although we have modified the boxes several times, before they were added to the stage or after that or even at both times, all updates are executed only onces per component.

RENDER Box1
RENDER Box2
RENDER Box3
RENDER Box4


Leave a Comment

You have a question or have experienced an issue? Please post it in the forum: http://sibirjak.tenderapp.com/ in order to make the discussion available at a more central place.