Call AMF RPC methods from Flash

If you would like consume some services through RemoteObject from Flash in the same fashion you do in Flex, you will need to follow these step:
  • Add the rpc.swc and framework.swc from your {FLEX_HOME}/frameworks/libs to you ptoject or .fla classpath

  • Optional: If you are compiling an Actionscript Project in Flash Builder you'll also need the resource bundles for those swcs. So add also the rpc_rb.swc and framework_rb.swc from {FLEX_HOME}/frameworks/locale to your classpath.

  • Look at the code below, the initializeRPC and registerClasses methods are what you need to make things work.


You are now ready to call remote methods via amf like this simple example.
Notes:
You will bring nearly 100kb of footprint to your swf due to dependencies from the flex framework. You reduce that footprint by registering a custom collection class instead of the ArrayCollection. The only requirement is that your collection class implements the IExternalizable interface.
import mx.collections.ArrayCollection;
import mx.core.mx_internal;
import mx.logging.Log;
import mx.logging.targets.TraceTarget;
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.messaging.config.ConfigMap;
import mx.messaging.config.LoaderConfig;
import mx.messaging.messages.AcknowledgeMessage;
import mx.messaging.messages.AcknowledgeMessageExt;
import mx.messaging.messages.CommandMessage;
import mx.messaging.messages.CommandMessageExt;
import mx.messaging.messages.ErrorMessage;
import mx.messaging.messages.RemotingMessage;
import mx.rpc.AbstractOperation;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;
import mx.utils.ObjectProxy;

use namespace mx_internal;

const ID:String = "my-amf";
const ENDPOINT:String = "http://your.domain/amf/gateway";
const DEFAULT_SOURCE:String = "your.service";
const DEFAULT_SOURCE:String = "your.service";

var remoteObject:RemoteObject;

/**
* This initializes the rpc library.
* After this you should see the requests going through.
* Note: LoaderConfig won't show up in the code hints, so you have to write down the import manually.
*/
function initializeRPC():void {

LoaderConfig.mx_internal::_url = loaderInfo.url;
LoaderConfig.mx_internal::_parameters = loaderInfo.parameters;
}

/**
* We register a bunch of class aliases that are needed by the rpc methods.
* The list *may be* incomplete, if you get some TypeCoertion error try registering that class.
*/
function registerClasses():void {

registerClassAlias("flex.messaging.messages.ErrorMessage", ErrorMessage);
registerClassAlias("flex.messaging.messages.CommandMessage", CommandMessage);
registerClassAlias("flex.messaging.messages.RemotingMessage", RemotingMessage);
registerClassAlias("flex.messaging.messages.AcknowledgeMessage", AcknowledgeMessage);
registerClassAlias("DSC", CommandMessageExt);
registerClassAlias("DSK", AcknowledgeMessageExt);
registerClassAlias("flex.messaging.config.ConfigMap", ConfigMap);
registerClassAlias("flex.messaging.io.ObjectProxy", ObjectProxy);
registerClassAlias("flex.messaging.io.ArrayCollection", ArrayCollection);
}

/**
* Prepare the RemoteObject, creating the appropriate AMF channel
* and setting up the default event handlers.
*/
function prepareDefaultRemoteObject():void {

var channel:AMFChannel = new AMFChannel(ID, ENDPOINT);

var channelSet:ChannelSet = new ChannelSet();
channelSet.addChannel(channel);

remoteObject = new RemoteObject();
remoteObject.source = DEFAULT_SOURCE;
remoteObject.destination = DEFAULT_DESTINATION;
remoteObject.channelSet = channelSet;
remoteObject.addEventListener(FaultEvent.FAULT, onDefaultServiceFault);
remoteObject.addEventListener(ResultEvent.RESULT, onDefaultServiceResult);
}

/**
* Create and execute the remote method.
*/
function makeAnonymousRemoteCall():void {

var operation:AbstractOperation = ro.getOperation("getProduct");
operation.send();
}


/**
* Handles faulty replies from the remote methods.
*/
function onDefaultServiceFault(event:FaultEvent):void {

trace(event.fault.faultDetail);
}

/**
* Handles successful replies from the remote methods.
*/
function onDefaultServiceResult(event:ResultEvent):void {

trace(event.result.toString());
}

// All RPC classes use the Flex logging API so enabling it in the Flash project could be helpful.
Log.addTarget(new TraceTarget());

initializeRPC();
registerClasses();

prepareDefaultRemoteObject();
makeAnonymousRemoteCall();

Align an a display object to its center

When you create dinamically a display object in ActionScript the registration point is at the top right (obviously it depends also on the way you design the object into the library) and expecially in Flex it can represent an issue when you want to align an object to a point considering the center of this object.
In order to avoid this issue it's enough to calculate the dispalcement between the center of the object and the actual X and Y coordinates, the following script is the function you have to use to calculate the diaplacement, attached there is a .fla file that show you a complete sample (please register in order to download the file).
function displacement(target:DisplayObjectContainer, x:Number, y:Number):Point{

var bounds:Rectangle = target.getBounds(this);

var centerX:Number = bounds.x + (bounds.width / 2);
var centerY:Number = bounds.y + (bounds.height / 2);

var deltaX:Number = target.x - centerX;
var deltaY:Number = target.y - centerY;

var point:Point = new Point();
point.x = x + deltaX;
point.y = y + deltaY;

return point;

}

Removing trailing nulls in ActionScript

When reading a byte array in ActionScript (like in C) the null value is read as a string termination. When you try use the readUTFBytes method over a ByteArray if the data contain a null value (0x00) the ActionScript interpreter stop the reading and retunr you a truncated string, in order to avoid this issue you can simply parse the data before using the method readUTFBytes.
Following a small sample where the variable bytedata represents the raw data recovered from your read operation.


var ba:ByteArray = new ByteArray();

for(var i:int = 0; i < bytedata.length; i++){

if(bytedata[i] != 0x00){

ba.writeByte(bytedata[i])

}else{

ba.writeByte(0x0A)

}

}

ba.position = 0;

var data:String = ba.readUTFBytes( ba.length );

Binary search

Binary search is a way of searching data inside a sorted list that can drastically reduce the time of the search operations.
In this example I'm searching for an integer because it's the most reliable data type in as. When comparing strings or complex objects some expedient should be taken to be sure that the comparison it's correct. The Comparable interface of Java could be a good solution.
To give insight about how fast is this method, to run 500 searchs in a list of 1000000 items the binarySearch took 2 to 5 milliseconds doing only 16-17 comparisons.
The regular indexOf method of the Array class took 450 milliseconds to perform the same task.
The bit operations (>>>) used to take the middle number (probe) also helped a bit, using a regular division (/2) increased the time to run the task to 4-9 milliseconds.
public function binarySearch(keys:Array, target:int):int
{
var high:int = keys.length;
var low:int = -1;

while (high - low > 1)
{
var probe:int = (low + high) >>> 1;
if (keys[probe] > target)
{
high = probe;
}
else
{
low = probe;
}
}

return (low == -1 || keys[low] !== target) ? -1 : low;
}

Replacing tokens in strings

A fast way to replace tokens in strings is to use the method replace of the mx.utils.StringUtil class.
The method will search for tokens with the pattern {n} and replace them with the parameters you give.
This is most effective when you have the string in your resource bundle and you need to inject some data into it.
var itemName:String = "Book";
var itemPrice:int = 10;

var localized:String = resourceManager.​getString("IB_store", "store.confirm");
trace(localized); // "You bought {0} for {1} €"

var message:String = StringUtil.substitute(localized, itemName, itemPrice);
trace(message); // "You bought Book for 10 €"

Escape special characters contained in the attribute of an XML stream

Regular Expressions are very powerful but sometime not so easy to use, recently we have had the need to get a deserialize an object that cames from a database and that contains in each node the name of the property, its value and its datatype.
The raw binary data that we got are expressed with the following syntax

<property name="crop" value="" type="com.gnstudio.rikorda.core.model.canvas.vo::Crop" >
<property name="points" value="" type="__AS3__.vec::Vector.<flash.geom::Point>" >
<property type="__AS3__.vec::Vector.<flash.geom::Point>" >
<property name="point" value = "" type = "flash.geom::Point">
<property name="x" value="" type="Number" />
<property name="y" value="" type="Number" /></property></property>



In the Flash Platform when you try to read this UTF bytes and cast them to an XML object you get an exception because there is the < and the > chars in the attribute, in order to solve this issue we used a regular expression to get the value stored in the attribute. The regular expression uses a named group to access this data into the exec method and inside this method there is the logic to replace the < and > chars with the right HTML entities.

Some useful links:
http://gnosis.cx/publish/programming/regular_expressions.html
http://www.radsoftware.com.au/articles/regexlearnsyntax.aspx
http://www.radsoftware.com.au/regexdesigner/
http://help.adobe.com/en_US/ActionScript/3.0_...3e3d118a9b90204-7e9a.html
http://livedocs.adobe.com/flex/3/html/help.ht...gular_Expressions_09.html
http://www.regular-expressions.info/examples.html
http://www.regular-expressions.info/brackets.html
http://wiki.tcl.tk/989
var content:String = '<property name="crop" value="" type="com.gnstudio.rikorda.core.model.canvas.vo::Crop" >' +
'<property name="points" value="" type="__AS3__.vec::Vector.<flash.geom::Point>" >' +
'<property type="__AS3__.vec::Vector.<flash.geom::Point>" >' +
'<property type="__AS3__.vec::Vector.<flash.geom::Point>" >' +
'<property name="point" value = "" type = "flash.geom::Point">' +
'<property name="x" value="" type="Number" />' +
'<property name="y" value="" type="Number" />' +
'</property>' +
'</property>';


trace("CONTENT BEFORE:", content, "\n");

var ltExp:RegExp = new RegExp("\\<", "g");
var gtExp:RegExp = new RegExp("\\>", "g");

var attributeName:String = "type";
var groupName:String = "attribute";

var attributes:RegExp = new RegExp("<(?:[^>\"']|\"[^\"]*\"|'[^']*')+?\\s" + attributeName + "\\s*=\\s*(?P<" + groupName + ">\"[^\"]*\"|'[^']*')(?:[^>\"']|\"[^\"]*\"|'[^']*')*>", "g");

var err:Error;
do {

try{

var data:String = attributes.exec(content)[groupName];
var clone:String = data;

var toReplace:Boolean;

if(clone.search(gtExp) > -1){

clone = clone.replace(gtExp, ">");
toReplace = true;

}

if(clone.search(ltExp) > -1){

clone = clone.replace(ltExp, "<");
toReplace = true;

}

if(toReplace){

var reg:RegExp = new RegExp("\\" + data, "g");
content = content.replace(reg, clone);

}

}catch(error:Error){

err = error
break;

}


} while ( !err);

trace("CONTENT AFTER:", content);

Detect paragraphs with the TextLayout framework

If you want to detect how many paragarphs are contained into an XML flow you can simpy use the power of E4X and the default namespace directive of ActionScript 3.0.
var data:XML = <TextFlow cffHinting="horizontalStem" color="#000000" columnCount="inherit" columnGap="inherit" columnWidth="inherit" 
fontFamily="Verdana" fontLookup="embeddedCFF" fontSize="26" lineBreak="inherit" paddingBottom="15" paddingLeft="15"
paddingRight="15" paddingTop="15" renderingMode="cff" verticalAlign="inherit"
whiteSpaceCollapse="preserve" xmlns="http://ns.adobe.com/textLayout/2008">
<p>
<span>
Enter your text here...
</span>
</p>
<p>
<span>
Enter your text here...
</span>
</p>
</TextFlow>;


var textFlowNamespace:Namespace = new Namespace("http://ns.adobe.com/textLayout/2008");
default xml namespace = textFlowNamespace;

trace(data.p.length())

Embedding fonts for Adobe TLF

To use embedded fonts with Adobe Text Layout Framework (TLF) you need to switch on the 'embedAsCFF' option and use Flex SDK 4.0 to compile the fonts. Please note that the 'embedAsCFF' attribute replaced the old 'cff' attribute in Flex SDK 4.0. After succesfully embedding the fonts make sure that you set the fontLookup property of the TextFLow to FontLookup.EMBEDDED_CFF.
// Embedding 'Anonymous Pro' font family
[Embed(source='/fonts/Anonymous Pro B.ttf', embedAsCFF='true', fontName='AnonymousPro', fontWeight='bold', fontStyle='normal', mimeType='application/x-font')]
private static var AnonymousProB:Class;

[Embed(source='/fonts/Anonymous Pro BI.ttf', embedAsCFF='true', fontName='AnonymousPro', fontWeight='bold', fontStyle='italic', mimeType='application/x-font')]
private static var AnonymousProBI:Class;

[Embed(source='/fonts/Anonymous Pro I.ttf', embedAsCFF='true', fontName='AnonymousPro', fontWeight='normal', fontStyle='italic', mimeType='application/x-font')]
private static var AnonymousProI:Class;

[Embed(source='/fonts/Anonymous Pro.ttf', embedAsCFF='true', fontName='AnonymousPro', fontWeight='normal', fontStyle='normal', mimeType='application/x-font')]
private static var AnonymousPro:Class;

// TextFlow options
textFlow.fontLookup = FontLookup.EMBEDDED_CFF;
textFlow.cffHinting = CFFHinting.HORIZONTAL_STEM;
textFlow.renderingMode = RenderingMode.CFF;

Add a custom StatusBar to a WindowedApplication

To use a custom component as a statusbar it's quiet easy,
just create your component using MXML or Actionscript, it's the same, and the add this line to your WindowedApplication
statusBarFactory="{new ClassFactory(MyCustomStatusBar)}"

Flex SDK 3.5 ComboBox issue

In the Flex SDK 3.5.0.12683 the ComboBox Class has a strange behavior, if you change the dataprovider after you have played with your ComboBox, the list of item in the dropdown is not refreshed.
Possible solutions to the problem are :
1) Switch back to the 3.4 SDK
2) Add the following few lines of code after updating the dataprovider : myComboBox.dropdown.dataProvider = yourArray;
Enjoy : )
myComboBox.dropdown.dataProvider = yourArray