Listing 1

package com.ebay.utils		{
	import mx.collections.ArrayCollection;

	public class ObjectTranslator			{

		import mx.utils.ArrayUtil;
		import flash.utils.*;
		import mx.core.DeferredInstanceFromClass;


		public function ObjectTranslator()			{
			//TODO: implement function

		}

		public static function translateArrayObjects(arrayOfObjects:Array,translateToClass:Class,upperCaseFromVar:
		Boolean=false):Array {
			// describeType describes a class and all of its properties
			var classInfo:XML = describeType(translateToClass);
			// get the class name, in order to instantiate more classes
			var className:String = classInfo.@name;
			var i:Number = new Number;
			var returnArray:Array = new Array;
			// get a reference to the class to create new classes
			var classRef:Class = Class(getDefinitionByName(className));
			var tempObj:Object = new classRef;

			if (arrayOfObjects != null)	{
				for (i=0;i<arrayOfObjects.length;i++)	{
					// for each new object, create a new class of the class type that was passed in.
					tempObj = new classRef();
					// send it off to translateObject function. send it the class to populate
					returnArray.push(translateObject(arrayOfObjects[i],tempObj,classInfo,upperCaseFromVar));
				}
			}
			return returnArray;  // return the translated array
		}

			
		public static function
	translateObject(translateFrom:Object,translateTo:Object,classInfo:XML=null,upperCaseFromVar:
	Boolean=false):Object  {
			var itemType:String;
			var itemName:String;
			var subClassInfo:XML;
			var tempArray:Array;
			var vType:String;
			var vName:String;
			var fromName:String;

			// if class information has already been sent in, we don't need to re-fetch it.
			if (classInfo == null) {
			 	classInfo = describeType(translateTo);
			}
			for each (var v:XML in classInfo..variable) {
	        	try 	{
	        		// xml values don't exactly match the variable name unless it knows for sure it's a string
	        		vName = v.@name;
	        		vType = v.@type;
	        		if (upperCaseFromVar)	{
	        			fromName = vName.toUpperCase();
	        		} else {
	        			fromName = vName;
	        		}
	        		// check to see if the untyped object has a property that matches the typed property
	        		// if it does, then translate it.  if not, skip it.
	        		// this is done to maintain integrity with the application and the internal classes.
	        		// internal classes are static (unless they've been made dynamic) an error will result if
	        		// and unknown variable is set in it.
	            	if (translateFrom.hasOwnProperty(vName) && translateFrom[vName] != null) 	{
		            	// Only need to check for a few types here. variables should come across
		            	// in a limited number of base object types, ie numbers, ints, dates, strings, etc.
		            	// anything else is assumed to be a class and will be sent for translation as well.
		            	// the one case that is not dealt with here is when an array of objects underneath
		            	// an array of objects.  This can be theoretically set up, but there's no clear
		            	// an array of objects has to be a special type of array, which in most cases only limits
		            	// the objects that can be store in it to a specific class.  the simplest way around
		            	// this is to send back a simple array of untyped objects which can then be translated
		            	// at the point that they are needed.

		            	switch (vType)  {
		            		case "Array":
		            			// in beta 3 SOAP sequences were typed as arrays by FLEX.
		            			if (translateFrom[vName] is ArrayCollection) 	{
		            				translateTo[vName] = translateFrom[fromName].source;
		            			} else if (translateFrom[fromName] is Array) 		{
		            				translateTo[vName] = translateFrom[fromName];
		            			}
		            			break;
		            		case "mx.collections::ArrayCollection":
		            			// in the GA release it looks like SOAP sequences are
		            			// typed as arrayCollections.  Support has been left in for both
		            			// types... just in case, so this function can be used for both types.
		            			if (translateFrom[fromName] is ArrayCollection) 	{
		            				translateTo[vName] = translateFrom[fromName];
		            			} else if (translateFrom[fromName] is Array) 		{
		            				translateTo[vName].source = translateFrom[fromName];
		            			}
		            			break;
		            		case "Number" :
		            			// sometimes numbers come across as a complexString.  this can
		            			// cause a mess at runtime if there is a need to check against a
		            			// simple number type.
		            			translateTo[vName] = new Number(translateFrom[fromName]);
		            			break;
		            		case "int" :
		            			// ditto for ints
		            			translateTo[vName] = new int(translateFrom[fromName]);
		            			break;
		            		case "uint" :
		            			// ditto for uints
		            			translateTo[vName] = new uint(translateFrom[fromName]);
		            			break;
		            		case "Date":
		            			// ditto for dates.  what's nice about this function is that with a little
		            			// work, dates can automatically be transfered from a UTC format or a string
		            			// format (used sometimes for easy transport)  into an actual date format
		            			// which should be easier to use.
		            			if (translateFrom[fromName] is Date) {
		            				translateTo[vName] = translateFrom[fromName];
		            			} else if (translateFrom[fromName] is String) {
		            				translateTo[vName] = parseDateFromString(translateFrom[fromName]);
		            			} else if (translateFrom[fromName] is Number || translateFrom[fromName] is int ||
								 translateFrom[fromName] is uint) {
		            				translateTo[vName] = parseUtcDate(translateFrom[fromName]);
		            			}
		            			break;
		            		case "Boolean":
		            			// Booleans get typed as a complexString sometimes too.
		            			translateTo[vName] = getBooleanValue(translateFrom[fromName]);
								break;
							case "String":
								translateTo[vName] = new String(translateFrom[fromName]);
								break;
							case "Object":
								translateTo[vName] = translateFrom[fromName];
								break;
		            		default :
		            			// assuming that anything that makes it here is a sub class...
		            			// send it to the this function again to decode it...
		            			translateTo[vName] = translateObject(translateFrom[fromName],translateTo[vName]);
		            			break;
		            	}
		            }
	            } catch (e:Error)  {
					// do nothing // we will loose data, but our app will stay in tact;
					//if (translateFrom.hasOwnProperty(v.@name)) 	{
					//	translateTo[v.@name] = translateFrom[v.@name];
					//}
					trace("an error occured in ObjectTranslator. The following fields were not captured correctly -
					 to:" + vName + "  from:" + fromName);
					trace(translateTo[vName]);
					trace(translateFrom[fromName]);
				}

	        }
			return translateTo;
		}
		
		public static function translateArrayCollection(collection:ArrayCollection,translateToClass:Class,
		upperCaseFromVar:Boolean=false):ArrayCollection 	{
			// this is a convenience function.  in the GA release of FLEX 2, objects are now coming back as
			// array collections.  if an array collection is expected, this function will check to make sure
			// that it is not null.  it returns a completely new collection, which may or may not be what is desired.
			var returnCollection:ArrayCollection = new ArrayCollection;
			if (collection != null) 	{
				if (collection.length > 0 )	{
					returnCollection.source =
					 translateArrayObjects(collection.source,translateToClass,upperCaseFromVar);
				}
			}
			return returnCollection;
		}

		public static function translateArrayCollectionAsArray(collection:ArrayCollection,translateToClass:Class,
		upperCaseFromVar:Boolean=false):Array	 	{
			// this is a convenience function.  in the GA release of FLEX 2, objects are now coming back as
			// array collections.  if an array collection is expected, this function will check to make sure
			// that it is not null.  it returns a completely new Array, which may or may not be what is desired
			var returnArray:Array = new Array;
			if (collection != null) 	{
				if (collection.length > 0 )	{
					returnArray = translateArrayObjects(collection.source,translateToClass,upperCaseFromVar);
				}
			}
			return returnArray;
		}

		public static function parseUtcDate(utcNumber:Number):Date	{
			var returnDate:Date;
			// there should be a minimum length here for a valid utc Date
			if (utcNumber.toString().length > 6)  {
				returnDate = new Date;
				returnDate.setTime(utcNumber);
			}
			return returnDate;
		}

		public static function parseDateFromString(dateString:String):Date 	{
			var returnDate:Date;
			// check to see if we have a utc date as a string
			// guessing that dateString should be at least 6 chars long in order to be
			// considered a viable date.... this could be a problem though, because theoretically,
			// a time of zero should still be valid.
			if (dateString.length > 6 && parseInt(dateString).toString().length > 6 ) 	{
				// we probaly have a utc date here
				returnDate = parseUtcDate(parseInt(dateString));
			} else {
				//  we didn't get a utc date so let's try to parse it...
				returnDate = parseUtcDate(Date.parse(dateString));
			}

			return returnDate;
		}

		public static function getBooleanValue(bValue:Object):Boolean		{
			var returnBoolean:Boolean = false;
			var stringBoolean:String = bValue.toString().toLowerCase();
			if (stringBoolean == "true" || stringBoolean == "yes" || stringBoolean == "1" || stringBoolean == "-1")	{
				returnBoolean = true;
			}
			return returnBoolean;
		}

		public static function clone(source:Object):*{
		    var myBA:ByteArray = new ByteArray();
		    myBA.writeObject(source);
		    myBA.position = 0;
		    return(myBA.readObject());
		}

	}
}