diff --git a/.gitignore b/.gitignore index 4e6fa9a..0f13692 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ bin dist # IDE crud. +.iml .project .idea .DS_Store diff --git a/README.mkd b/README.mkd index d0c65c7..52da199 100644 --- a/README.mkd +++ b/README.mkd @@ -1,14 +1,14 @@ AS3 Vanilla =========== -Download v0.1.3 Release Archive: [as3-vanilla-0.1.3.zip](https://dl.dropbox.com/u/4551551/github/as3-vanilla/as3-vanilla-0.1.3.zip) +Download v0.2.0 Release Archive: [as3-vanilla-0.2.0.swc](hhttps://dl.dropboxusercontent.com/u/22095429/maven/org/osflash/as3-vanilla/0.2.0/as3-vanilla-0.2.0.zip) A lightweight library which enables a developer to extract a strongly typed Model Object from a untyped dynamic object without having to write a single line of parsing or marshalling code. An example use case would be turning data returned from a JSON endpoint into a Model, if you've ever written the following code, then you can benefit from this library: -```actionscript +``` // Use a JSON library to convert a JSON String into an AS3 Object. var jsonObject : Object = JSON.decode('{"name":"Jonny", "age": 28, "music":["nin","mew"]}'); @@ -23,7 +23,7 @@ trace(myPerson.name) // Jonny. Using the Vanilla library, you can turn the above code into this: -```actionscript +``` // Use a JSON library to convert a JSON String into an AS3 Object. var jsonObject : Object = JSON.decode('{"name":"Jonny", "age": 28, "music":["nin","mew"]}'); @@ -35,7 +35,7 @@ trace(myPerson.name); // "Jonny" Things get even easier when you make use of the `extract` convenience method: -```actionscript +``` var jsonObject : Object = JSON.decode('{"name":"Jonny", "age": 28, "music":["nin","mew"]}'); var myPerson : PersonVO = extract(jsonObject, PersonVO); ``` @@ -43,7 +43,7 @@ var myPerson : PersonVO = extract(jsonObject, PersonVO); Got a complex object graph? Well that's where Vanilla really shines, making light work of parsing and marshalling nested objects, for example: -```actionscript +``` // The PersonVO Model contains an 'address' field which is a complex datatype (AddressVO). package app.model { class PersonVO { @@ -77,7 +77,7 @@ Mapping Fields Sometimes the fields on your Model object don't quite match up to the fields in your source object; not a problem, you can use Metadata to define the mappings in your Model object: -```actionscript +``` package app.model { public class PersonVO { public var name : String; @@ -93,7 +93,7 @@ Mapping to Constructor Arguments If you're a fan of immutable models then you will want to define some Metadata to map the fields in your source object to the constructor arguments of your Model object: -```actionscript +``` package app.model { // Don't forget, constructor metadata is annotated to the class, not the constructor method! [Marshall(field="name", field="age", field="music")] @@ -116,7 +116,7 @@ Mapping to Methods / Mutators ----------------------------- You can map the fields of your source object to a setter method in your Model object: -```actionscript +``` package app.model { public class PersonModel { private var _name : String; @@ -141,7 +141,7 @@ Ignoring fields ----------------------------- You can ignore fields by using [Transient] metadata: -```actionscript +``` package app.model { public class PersonModel { [Transient] @@ -158,7 +158,7 @@ Automatic Coercion Vanilla will automatically coerce Arrays found in your source object into Vectors defined in your target Model Class, take the following example: -```actionscript +``` // The Target Model Class Definition. class app.model { public class ColourList { @@ -176,7 +176,7 @@ trace(getQualifiedClassName(result.colours)); // __AS3__.vec::Vector. If you aren't using Vectors in your project, then you will have to give Vanilla a hand and provide it with a type hint as to what the Array is expected to contain, for example: -```actionscript +``` // The Target Model Class Definition. class app.model { public class Contact { @@ -209,22 +209,52 @@ trace(result.phoneNumbers); // [object PhoneNumber],[object PhoneNumber] trace((result.phoneNumbers[0] as PhoneNumber).type) // "home". ``` +Enum support +------------ +Java style Enum support is implemented using the IEnum interface, which marks a class for being processed as an Enum. +An As3 example enum: -Dependencies ------------- -`as3commons-reflect` is used for all reflection; in order to use as3-vanilla you will need to download and add -the following SWCs to your project's libs folder. +``` +public class LocaleEnum implements IEnum { + + public static var DA:LocaleEnum = new LocaleEnum("DA"); + public static var EN:LocaleEnum = new LocaleEnum("EN"); + + private var localeName:String; + + function LocaleEnum(localeName:String) { + this.localeName = localeName; + } +} +``` + +``` +// The Target Model Class Definition. +class app.model { + public class Locale { + public var supportedLocale : LocaleEnum; + } +} -* [ascommons-reflect-1.4.1](http://projects.yoolab.org/maven/content/repositories/releases/org/as3commons/as3commons-reflect/1.4.1/as3commons-reflect-1.4.1.swc) -* [as3commons-lang-0.3.4](http://projects.yoolab.org/maven/content/repositories/releases/org/as3commons/as3commons-lang/0.3.4/as3commons-lang-0.3.4.swc) -* [as3commons-logging-2.0](http://projects.yoolab.org/maven/content/repositories/releases/org/as3commons/as3commons-logging/2.0/as3commons-logging-2.0.swc) +var source : Object = { supportedLocale:"DA" }; +var result : Locale = extract(source, Locale); +trace(getQualifiedClassName(result.supportedLocale)); // LocaleEnum + + +``` + + + +Dependencies +------------ +* [ascommons-reflect-1.6.4](https://as3-commons.googlecode.com/files/as3commons-reflect-1.6.4.swc) +* [as3commons-lang-0.3.7](https://as3-commons.googlecode.com/files/as3commons-lang-0.3.7.swc) Future Features / Wish List --------------------------- * The user should be able to define mapping rules without having to use Metadata. -* Caching of the InjectionMap (assuming Class definitions won't change at run time). * Java style mutators (eg: `setFoo()`) could be mapped automatically. * By using `as3commons-bytecode` we could perform ByteCode reflection to retrieve the names of parameters; this would would remove the need for constructor marshalling metadata. diff --git a/as3-vanilla.iml b/as3-vanilla.iml new file mode 100644 index 0000000..d694cb5 --- /dev/null +++ b/as3-vanilla.iml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build.properties b/build.properties deleted file mode 100644 index c53fc3c..0000000 --- a/build.properties +++ /dev/null @@ -1,16 +0,0 @@ -# If you don't want to use an environment variable for your FLEX_HOME, you can define FLEX_HOME in a `user.properties` -# file instead. -FLEX_HOME = ${env.FLEX_HOME} - -# Versioning -project.name = as3-vanilla -project.version = 0.1.3 -project.versionedname = ${project.name}-${project.version} - -# Folder layout -test-src.dir = test-src -src.dir = src -bin.dir = bin -report.dir = ${bin.dir}/report -lib.dir = libs -dist.dir = dist diff --git a/build.xml b/build.xml deleted file mode 100644 index 7b8fea9..0000000 --- a/build.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/libs/as3commons-lang-0.3.4.swc b/libs/as3commons-lang-0.3.4.swc deleted file mode 100644 index a827479..0000000 Binary files a/libs/as3commons-lang-0.3.4.swc and /dev/null differ diff --git a/libs/as3commons-logging-2.0.swc b/libs/as3commons-logging-2.0.swc deleted file mode 100644 index c9daf93..0000000 Binary files a/libs/as3commons-logging-2.0.swc and /dev/null differ diff --git a/libs/as3commons-reflect-1.4.1.swc b/libs/as3commons-reflect-1.4.1.swc deleted file mode 100644 index df1faaf..0000000 Binary files a/libs/as3commons-reflect-1.4.1.swc and /dev/null differ diff --git a/libs/flexunit-4.0.0.swc b/libs/flexunit-4.0.0.swc deleted file mode 100644 index 9ec3c7d..0000000 Binary files a/libs/flexunit-4.0.0.swc and /dev/null differ diff --git a/libs/flexunit-cilistener-4.0.0.swc b/libs/flexunit-cilistener-4.0.0.swc deleted file mode 100644 index 97d469e..0000000 Binary files a/libs/flexunit-cilistener-4.0.0.swc and /dev/null differ diff --git a/libs/flexunitTasks-4.0.0.jar b/libs/flexunitTasks-4.0.0.jar deleted file mode 100644 index c74141a..0000000 Binary files a/libs/flexunitTasks-4.0.0.jar and /dev/null differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c6c81dc --- /dev/null +++ b/pom.xml @@ -0,0 +1,233 @@ + + + 4.0.0 + + org.osflash + as3-vanilla + swc + 0.2.0-SNAPSHOT + + + UTF-8 + + 4.13.0.20140701 + 15.0 + 26 + + + + src/main/as3 + src/test/as3 + + + + org.apache.maven.plugins + maven-source-plugin + 2.4 + + + attach-sources + + jar + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.9 + + + unpack + process-test-classes + + unpack + + + + + com.adobe.flash.player + flashplayer-debugger + mac + 15.0.0.239 + zip + false + ** + ${project.basedir}/bin/flashplayer/mac/ + + + + + + + + + net.flexmojos.oss + flexmojos-maven-plugin + 7.1.0-SNAPSHOT + true + true + + true + true + ${project.basedir}/bin/flashplayer/mac/Flash Player.app/Contents/MacOS/Flash Player Debugger + ${playerglobal.version} + ${swf.version} + na + true + true + + Marshall + Transient + + + false + + + + + org.apache.flex + compiler + ${flex.version} + pom + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.1 + + true + @{project.version} + true + + + + + + + + com.adobe.flash.framework + playerglobal + ${playerglobal.version} + swc + + + + org.apache.flex + framework + ${flex.version} + pom + compile + + + + org.as3commons + as3commons-lang + 0.3.7 + swc + merged + + + + org.as3commons + as3commons-reflect + 1.6.4 + swc + merged + + + + + + org.flexunit + flexunit + 4.2.0-20140410 + as3_4.12.0 + swc + test + + + + org.mockolate + mockolate + 0.12.4 + as3 + swc + test + + + + org.hamcrest + hamcrest-as3 + 1.2.0 + as3 + swc + test + + + + + + brightware-repo + https://dl.dropboxusercontent.com/u/22095429/maven + + true + + + + + sonatype-repo + https://oss.sonatype.org/content/repositories/snapshots + + true + daily + + + + + + + brightware-repo + https://dl.dropboxusercontent.com/u/22095429/maven + + true + + + false + + + + + sonatype-repo + https://oss.sonatype.org/content/repositories/snapshots + + true + + + true + daily + + + + + + + releases-repo + Releases Repo + file:///Users/sorenjepsen/Dropbox/Public/maven/ + + + + + scm:git:git@github.com:natami/as3-vanilla.git + scm:git:git@github.com:natami/as3-vanilla.git + scm:git:git@github.com:natami/as3-vanilla.git + + \ No newline at end of file diff --git a/src/main/as3/org/osflash/vanilla/IEnum.as b/src/main/as3/org/osflash/vanilla/IEnum.as new file mode 100644 index 0000000..847339f --- /dev/null +++ b/src/main/as3/org/osflash/vanilla/IEnum.as @@ -0,0 +1,8 @@ +package org.osflash.vanilla +{ + + public interface IEnum + { + + } +} diff --git a/src/org/osflash/vanilla/InjectionDetail.as b/src/main/as3/org/osflash/vanilla/InjectionDetail.as similarity index 82% rename from src/org/osflash/vanilla/InjectionDetail.as rename to src/main/as3/org/osflash/vanilla/InjectionDetail.as index b049092..7f3e515 100644 --- a/src/org/osflash/vanilla/InjectionDetail.as +++ b/src/main/as3/org/osflash/vanilla/InjectionDetail.as @@ -1,45 +1,56 @@ package org.osflash.vanilla { - internal class InjectionDetail + + public class InjectionDetail { - private var _fieldName : String; - private var _type : Class; - private var _required : Boolean; - private var _arrayTypeHint : Class; - - public function InjectionDetail(fieldName : String, type : Class, required : Boolean, arrayTypeHint : Class) { + public function InjectionDetail(fieldName : String, type : Class, required : Boolean, arrayTypeHint : Class) + { _fieldName = fieldName; _type = type; _required = required; _arrayTypeHint = arrayTypeHint; } - - public function get name() : String { - return _fieldName; - } + + + private var _fieldName : String; + private var _required : Boolean; + + private var _type : Class; public function get type() : Class { return _type; } - + + + private var _arrayTypeHint : Class; + + public function get arrayTypeHint() : Class + { + return _arrayTypeHint; + } + + + public function get name() : String + { + return _fieldName; + } + + public function get isRequired() : Boolean { return _required; } + public function toString() : String { var result : String = _fieldName + "<" + type + ">"; - if (arrayTypeHint) { + if (arrayTypeHint) + { result += "->" + arrayTypeHint; } return result; } - - public function get arrayTypeHint() : Class - { - return _arrayTypeHint; - } } } diff --git a/src/org/osflash/vanilla/InjectionMap.as b/src/main/as3/org/osflash/vanilla/InjectionMap.as similarity index 78% rename from src/org/osflash/vanilla/InjectionMap.as rename to src/main/as3/org/osflash/vanilla/InjectionMap.as index be68f43..daeca17 100644 --- a/src/org/osflash/vanilla/InjectionMap.as +++ b/src/main/as3/org/osflash/vanilla/InjectionMap.as @@ -1,72 +1,87 @@ package org.osflash.vanilla { - internal class InjectionMap + + public class InjectionMap { + private static function getNames(object : Object) : Array + { + const result : Array = []; + for (var name : String in object) + { + result.push(name); + } + return result; + } + + private var _constructorFields : Array = []; private var _fields : Object = {}; private var _methods : Object = {}; + public function addConstructorField(injectionDetails : InjectionDetail) : void { _constructorFields.push(injectionDetails); } + public function getConstructorFields() : Array { return _constructorFields; } + public function addField(fieldName : String, injectionDetails : InjectionDetail) : void { _fields[fieldName] = injectionDetails; } - + + public function getFieldNames() : Array { - const result : Array = []; - for (var fieldName : String in _fields) { - result.push(fieldName); - } - return result; + return getNames(_fields); } - - public function getField(fieldName : String) : InjectionDetail { + + + public function getField(fieldName : String) : InjectionDetail + { return _fields[fieldName]; } + public function addMethod(methodName : String, injectionDetails : InjectionDetail) : void { _methods[methodName] ||= []; (_methods[methodName] as Array).push(injectionDetails); - } - + } + + public function getMethodsNames() : Array { - const result : Array = []; - for (var methodName : String in _methods) { - result.push(methodName); - } - return result; + return getNames(_methods); } - + + public function getMethod(methodName : String) : Array { return _methods[methodName]; } - + + public function toString() : String { var result : String = "[FieldMap "; - - result += "ctor:{" + _constructorFields +"}, "; + + result += "ctor:{" + _constructorFields + "}, "; result += "fields:{" + _fields + "}, "; - + result += "methods:{"; - for (var methodName : String in _methods) { + for (var methodName : String in _methods) + { result += methodName + "(" + getMethod(methodName) + "),"; } result += "}"; - + result += "]"; return result; } diff --git a/src/org/osflash/vanilla/MarshallingError.as b/src/main/as3/org/osflash/vanilla/MarshallingError.as similarity index 76% rename from src/org/osflash/vanilla/MarshallingError.as rename to src/main/as3/org/osflash/vanilla/MarshallingError.as index ce44410..c8a3e2d 100644 --- a/src/org/osflash/vanilla/MarshallingError.as +++ b/src/main/as3/org/osflash/vanilla/MarshallingError.as @@ -1,11 +1,14 @@ package org.osflash.vanilla { + public class MarshallingError extends Error { public static const TYPE_MISMATCH : uint = 150000; public static const MISSING_REQUIRED_FIELD : uint = 150001; - - public function MarshallingError(message : String, id : uint) { + + + public function MarshallingError(message : String, id : uint) + { super(message, id); } } diff --git a/src/org/osflash/vanilla/VANILLA_INSTANCE.as b/src/main/as3/org/osflash/vanilla/VANILLA_INSTANCE.as similarity index 94% rename from src/org/osflash/vanilla/VANILLA_INSTANCE.as rename to src/main/as3/org/osflash/vanilla/VANILLA_INSTANCE.as index e078e69..913f53e 100644 --- a/src/org/osflash/vanilla/VANILLA_INSTANCE.as +++ b/src/main/as3/org/osflash/vanilla/VANILLA_INSTANCE.as @@ -1,7 +1,8 @@ package org.osflash.vanilla { + /** - * Global, singleton instance used by the extract() convenience method. + * Global, singleton instance used by the extract() convenience method. */ internal var VANILLA_INSTANCE : Vanilla = new Vanilla(); } diff --git a/src/main/as3/org/osflash/vanilla/Vanilla.as b/src/main/as3/org/osflash/vanilla/Vanilla.as new file mode 100644 index 0000000..032b5e6 --- /dev/null +++ b/src/main/as3/org/osflash/vanilla/Vanilla.as @@ -0,0 +1,201 @@ +package org.osflash.vanilla +{ + + import avmplus.getQualifiedClassName; + + import org.as3commons.lang.ClassUtils; + import org.as3commons.lang.ObjectUtils; + import org.as3commons.reflect.Type; + import org.osflash.vanilla.caching.InjectionMapCache; + + public class Vanilla + { + + private const injectionMapCache : InjectionMapCache = InjectionMapCache.getInstance(); + + + private static function instantiateClass(targetType : Class, ctorArgs : Array) : * + { + return ClassUtils.newInstance(targetType, ctorArgs); + } + + + private static function instantiateEnum(targetType : Class, source : Object) : * + { + return targetType[source]; + } + + + private static function isVector(obj : *) : Boolean + { + return (getQualifiedClassName(obj).indexOf('__AS3__.vec::Vector') == 0); + } + + + private static function isEnum(clazz : Class) : Boolean + { + return ClassUtils.isImplementationOf(clazz, IEnum); + } + + + /** + * Attempts to extract properties from the supplied source object into an instance of the supplied targetType. + * + * @param source Object which contains properties that you wish to transfer to a new instance of the + * supplied targetType Class. + * @param targetType The target Class of which an instance will be returned. + * @return An instance of the supplied targetType containing all the properties extracted from + * the supplied source object. + */ + public function extract(source : Object, targetType : Class) : * + { + // Catch the case where we've been asked to extract a value which is already of the intended targetType; + // this can often happen when Vanilla is recursing, in which case there is nothing to do. + if (source is targetType) + { + return source; + } + + // Construct an InjectionMap which tells us how to inject fields from the source object into + // the Target class. + const injectionMap : InjectionMap = injectionMapCache.getInjectionMapForClass(targetType); + + // Create a new instance of the targetType; and then inject the values from the source object into it + return instantiateAndInjectValues(source, targetType, injectionMap); + } + + + private function instantiateAndInjectValues(source : *, targetType : Class, injectionMap : InjectionMap) : * + { + var target : *; + + if (isEnum(targetType)) + { + target = instantiateEnum(targetType, source); + } else if (isVector(targetType)) + { + target = extractVector(source, targetType, Type.forClass(targetType).parameters[0]); + } else + { + target = instantiateClass(targetType, fetchConstructorArgs(source, injectionMap)); + + injectFields(source, target, injectionMap); + injectMethods(source, target, injectionMap); + } + + return target; + } + + + private function fetchConstructorArgs(source : Object, injectionMap : InjectionMap) : Array + { + const result : Array = []; + const constructorFields : Array = injectionMap.getConstructorFields(); + for (var i : uint = 0 ; i < constructorFields.length ; i++) + { + var injectionDetail : InjectionDetail = constructorFields[i]; + result.push(extractValue(source, injectionDetail)); + } + return result; + } + + + private function injectFields(source : Object, target : *, injectionMap : InjectionMap) : void + { + const fieldNames : Array = injectionMap.getFieldNames(); + for each (var fieldName : String in fieldNames) + { + const injectionDetail : InjectionDetail = injectionMap.getField(fieldName); + target[fieldName] = extractValue(source, injectionDetail); + } + } + + + private function injectMethods(source : Object, target : *, injectionMap : InjectionMap) : void + { + const methodNames : Array = injectionMap.getMethodsNames(); + for each (var methodName : String in methodNames) + { + const values : Array = []; + for each (var injectionDetail : InjectionDetail in injectionMap.getMethod(methodName)) + { + values.push(extractValue(source, injectionDetail)); + } + (target[methodName] as Function).apply(null, values); + } + } + + + private function extractValue(source : Object, injectionDetail : InjectionDetail) : * + { + var value : * = source[injectionDetail.name]; + + // Is this a required injection? + if (injectionDetail.isRequired && value === undefined) + { + throw new MarshallingError("Required value " + injectionDetail + " does not exist in the source object.", MarshallingError.MISSING_REQUIRED_FIELD); + } + + if (value) + { + // automatically coerce simple types. + if (!ObjectUtils.isSimple(value)) + { + value = extract(value, injectionDetail.type); + } + + // Collections are harder, we need to coerce the contents. + else if (value is Array) + { + if (isVector(injectionDetail.type)) + { + value = extractVector(value, injectionDetail.type, injectionDetail.arrayTypeHint); + } else if (injectionDetail.arrayTypeHint) + { + value = extractTypedArray(value, injectionDetail.arrayTypeHint); + } + } else if (isEnum(injectionDetail.type)) + { + value = extract(value, injectionDetail.type); + } + + // refuse to allow any automatic coercing to occur. + if (!(value is injectionDetail.type)) + { + throw new MarshallingError("Could not coerce `" + injectionDetail.name + "` (value: " + value + " <" + Type.forInstance(value).clazz + "]>) from source object to " + injectionDetail.type + " on target object", MarshallingError.TYPE_MISMATCH); + } + } + + return value; + } + + + private function extractTypedArray(source : Array, targetClassType : Class) : Array + { + const result : Array = new Array(source.length); + for (var i : uint = 0 ; i < source.length ; i++) + { + result[i] = extract(source[i], targetClassType); + } + return result; + } + + + private function extractVector(source : Array, targetVectorClass : Class, targetClassType : Class) : * + { + const result : * = ClassUtils.newInstance(targetVectorClass); + for (var i : uint = 0 ; i < source.length ; i++) + { + if (isVector(targetClassType)) + { + var classType : Type = Type.forClass(targetClassType); + result[i] = extractVector(source[i], targetClassType, classType.parameters[0]); + } else + { + result[i] = extract(source[i], targetClassType); + } + } + return result; + } + } +} \ No newline at end of file diff --git a/src/main/as3/org/osflash/vanilla/caching/InjectionMapCache.as b/src/main/as3/org/osflash/vanilla/caching/InjectionMapCache.as new file mode 100644 index 0000000..5033c6d --- /dev/null +++ b/src/main/as3/org/osflash/vanilla/caching/InjectionMapCache.as @@ -0,0 +1,88 @@ +package org.osflash.vanilla.caching +{ + + import flash.errors.IllegalOperationError; + + import org.as3commons.reflect.Type; + import org.osflash.vanilla.InjectionMap; + import org.osflash.vanilla.util.*; + + public class InjectionMapCache + { + + private static var instance : InjectionMapCache; + + + public static function getInstance() : InjectionMapCache + { + if (!instance) + { + instance = new InjectionMapCache(SingletonLock); + } + + return instance; + } + + + private static function createInjectionMapForClass(targetType : Class) : InjectionMap + { + const injectionMap : InjectionMap = new InjectionMap(); + const reflectionMap : Type = Type.forClass(targetType); + + addReflectionRules(injectionMap, reflectionMap); + + return injectionMap; + } + + + private static function addReflectionRules(injectionMap : InjectionMap, reflectionMap : Type) : void + { + ReflectionRulesHelper.addReflectedConstructorRules(injectionMap, reflectionMap); + ReflectionRulesHelper.addReflectedFieldRules(injectionMap, reflectionMap); + ReflectionRulesHelper.addReflectedSetterRules(injectionMap, reflectionMap); + } + + + public function InjectionMapCache(key : Class) + { + if (key != SingletonLock) + { + throw new IllegalOperationError("InjectionMapCache can only be accessed through InjectionMapCache.getInstance()"); + } + } + + + private var _injectionCache : ICache = new CacheUtil(); + + internal function get injectionCache() : ICache + { + return _injectionCache; + } + + + internal function set injectionCache(value : ICache) : void + { + _injectionCache = value; + } + + + public function getInjectionMapForClass(targetType : Class) : InjectionMap + { + if (injectionCache.hasElement(targetType)) + { + return injectionCache.getElement(targetType); + } + + const injectionMap : InjectionMap = createInjectionMapForClass(targetType); + + injectionCache.addElement(targetType, injectionMap); + + return injectionMap; + } + } +} + +internal class SingletonLock +{ + +} \ No newline at end of file diff --git a/src/main/as3/org/osflash/vanilla/extract.as b/src/main/as3/org/osflash/vanilla/extract.as new file mode 100644 index 0000000..21d3a71 --- /dev/null +++ b/src/main/as3/org/osflash/vanilla/extract.as @@ -0,0 +1,17 @@ +package org.osflash.vanilla +{ + + /** + * Attempts to extract properties from the supplied source object into an instance of the supplied targetType. + * + * @param source Object which contains properties that you wish to transfer to a new instance of the + * supplied targetType Class. + * @param targetType The target Class of which an instance will be returned. + * @return An instance of the supplied targetType containing all the properties extracted from + * the supplied source object. + */ + public function extract(source : Object, targetType : Class) : * + { + return VANILLA_INSTANCE.extract(source, targetType); + } +} diff --git a/src/main/as3/org/osflash/vanilla/util/CacheUtil.as b/src/main/as3/org/osflash/vanilla/util/CacheUtil.as new file mode 100644 index 0000000..dd2e7e0 --- /dev/null +++ b/src/main/as3/org/osflash/vanilla/util/CacheUtil.as @@ -0,0 +1,44 @@ +/** + * Created by sorenjepsen on 29/12/13. + */ +package org.osflash.vanilla.util +{ + + import flash.utils.Dictionary; + + import org.as3commons.lang.IllegalArgumentError; + + public class CacheUtil implements ICache + { + + public function CacheUtil() + { + } + + + private var cacheDictionary : Dictionary = new Dictionary(); + + + public function addElement(key : *, value : *) : void + { + cacheDictionary[key] = value; + } + + + public function getElement(key : *) : * + { + if (!hasElement(key)) + { + throw new IllegalArgumentError("No cached element found for key: " + key); + } + + return cacheDictionary[key]; + } + + + public function hasElement(key : *) : Boolean + { + return cacheDictionary[key] != null; + } + } +} diff --git a/src/main/as3/org/osflash/vanilla/util/ICache.as b/src/main/as3/org/osflash/vanilla/util/ICache.as new file mode 100644 index 0000000..7ae04d2 --- /dev/null +++ b/src/main/as3/org/osflash/vanilla/util/ICache.as @@ -0,0 +1,16 @@ +package org.osflash.vanilla.util +{ + + public interface ICache + { + + function addElement(key : *, value : *) : void; + + + function getElement(key : *) : *; + + + function hasElement(key : *) : Boolean; + + } +} diff --git a/src/main/as3/org/osflash/vanilla/util/ReflectionRulesHelper.as b/src/main/as3/org/osflash/vanilla/util/ReflectionRulesHelper.as new file mode 100644 index 0000000..125afa8 --- /dev/null +++ b/src/main/as3/org/osflash/vanilla/util/ReflectionRulesHelper.as @@ -0,0 +1,158 @@ +package org.osflash.vanilla.util +{ + + import org.as3commons.lang.ClassUtils; + import org.as3commons.reflect.Accessor; + import org.as3commons.reflect.Field; + import org.as3commons.reflect.Metadata; + import org.as3commons.reflect.MetadataArgument; + import org.as3commons.reflect.Method; + import org.as3commons.reflect.Parameter; + import org.as3commons.reflect.Type; + import org.as3commons.reflect.Variable; + import org.osflash.vanilla.*; + + public class ReflectionRulesHelper + { + + private static const METADATA_MARSHALL_TAG : String = "Marshall"; + private static const METADATA_FIELD_KEY : String = "field"; + private static const METADATA_TYPE_KEY : String = "type"; + + + public static function addReflectedConstructorRules(injectionMap : InjectionMap, reflectionMap : Type) : void + { + const clazzMarshallingMetadata : Array = reflectionMap.getMetadata(METADATA_MARSHALL_TAG); + if (!clazzMarshallingMetadata) + { + return; + } + + const marshallingMetadata : Metadata = clazzMarshallingMetadata[0]; + const numArgs : uint = marshallingMetadata.arguments.length; + + for (var i : uint = 0 ; i < numArgs ; i++) + { + var argument : MetadataArgument = MetadataArgument(marshallingMetadata.arguments[i]); + if (argument.key == METADATA_FIELD_KEY) + { + const param : Parameter = reflectionMap.constructor.parameters[i]; + const arrayTypeHint : Class = extractArrayTypeHint(param.type); + injectionMap.addConstructorField(new InjectionDetail(argument.value, param.type.clazz, true, arrayTypeHint)); + } + } + } + + + public static function addReflectedFieldRules(injectionMap : InjectionMap, reflectionMap : Type) : void + { + for each (var field : Field in reflectionMap.fields) + { + if (!field.hasMetadata(Metadata.TRANSIENT) && canAccess(field)) + { + const fieldMetadataEntries : Array = field.getMetadata(METADATA_MARSHALL_TAG); + const fieldMetadata : Metadata = (fieldMetadataEntries) ? fieldMetadataEntries[0] : null; + const arrayTypeHint : Class = extractArrayTypeHint(field.type, fieldMetadata); + const sourceFieldName : String = extractFieldName(field, fieldMetadata); + + injectionMap.addField(field.name, new InjectionDetail(sourceFieldName, field.type.clazz, false, arrayTypeHint)); + } + } + } + + + public static function addReflectedSetterRules(injectionMap : InjectionMap, reflectionMap : Type) : void + { + for each (var method : Method in reflectionMap.methods) + { + + const methodMarshallingMetadata : Array = method.getMetadata(METADATA_MARSHALL_TAG); + if (methodMarshallingMetadata == null) + { + continue; + } + + const metadata : Metadata = methodMarshallingMetadata[0]; + const numArgs : uint = metadata.arguments.length; + + for (var i : uint = 0 ; i < numArgs ; i++) + { + var argument : MetadataArgument = metadata.arguments[i]; + if (argument.key == METADATA_FIELD_KEY) + { + const param : Parameter = method.parameters[i]; + const arrayTypeHint : Class = extractArrayTypeHint(param.type, metadata); + injectionMap.addMethod(method.name, new InjectionDetail(argument.value, param.type.clazz, false, arrayTypeHint)); + } + } + } + } + + + private static function canAccess(field : Field) : Boolean + { + if (field is Variable) + { + return true; + } else if (field is Accessor) + { + return (field as Accessor).writeable; + } + return false; + } + + + private static function extractArrayTypeHint(typeHint : Type, metadata : Metadata = null) : Class + { + // Vectors carry their own typeHint hint. + if (typeHint.parameters && typeHint.parameters[0] is Class) + { + return typeHint.parameters[0]; + } + + // Otherwise we will look for some "typeHint" metadata, if it was defined. + else if (metadata) + { + const numArgs : uint = metadata.arguments.length; + for (var i : uint = 0 ; i < numArgs ; i++) + { + var argument : MetadataArgument = metadata.arguments[i]; + if (argument.key == METADATA_TYPE_KEY) + { + const clazz : Class = ClassUtils.forName(argument.value); + return clazz; + } + } + } + + // No typeHint hint. + return null; + } + + + private static function extractFieldName(field : Field, metadata : Metadata) : String + { + // See if a taget fieldName has been defined in the Metadata. + if (metadata) + { + const numArgs : uint = metadata.arguments.length; + for (var i : uint = 0 ; i < numArgs ; i++) + { + var argument : MetadataArgument = metadata.arguments[i]; + if (argument.key == METADATA_FIELD_KEY) + { + return argument.value; + } + } + } + + // Assume it's a 1 to 1 mapping. + return field.name; + } + + + public function ReflectionRulesHelper() + { + } + } +} diff --git a/src/org/osflash/vanilla/Vanilla.as b/src/org/osflash/vanilla/Vanilla.as deleted file mode 100644 index c099603..0000000 --- a/src/org/osflash/vanilla/Vanilla.as +++ /dev/null @@ -1,266 +0,0 @@ -package org.osflash.vanilla -{ - import org.as3commons.lang.ClassUtils; - import org.as3commons.lang.ObjectUtils; - import org.as3commons.reflect.Accessor; - import org.as3commons.reflect.Field; - import org.as3commons.reflect.Metadata; - import org.as3commons.reflect.MetadataArgument; - import org.as3commons.reflect.Method; - import org.as3commons.reflect.Parameter; - import org.as3commons.reflect.Type; - import org.as3commons.reflect.Variable; - - import flash.utils.getQualifiedClassName; - - public class Vanilla - { - private static const METADATA_TAG : String = "Marshall"; - private static const METADATA_FIELD_KEY : String = "field"; - private static const METADATA_TYPE_KEY : String = "type"; - - /** - * Attempts to extract properties from the supplied source object into an instance of the supplied targetType. - * - * @param source Object which contains properties that you wish to transfer to a new instance of the - * supplied targetType Class. - * @param targetType The target Class of which an instance will be returned. - * @return An instance of the supplied targetType containing all the properties extracted from - * the supplied source object. - */ - public function extract(source : Object, targetType : Class) : * - { - // Catch the case where we've been asked to extract a value which is already of the intended targetType; - // this can often happen when Vanilla is recursing, in which case there is nothing to do. - if (source is targetType) { - return source; - } - - // Construct an InjectionMap which tells us how to inject fields from the source object into - // the Target class. - const injectionMap : InjectionMap = new InjectionMap(); - addReflectedRules(injectionMap, targetType, Type.forClass(targetType)); - - // Create a new isntance of the targetType; and then inject the values from the source object into it - const target : * = instantiate(targetType, fetchConstructorArgs(source, injectionMap.getConstructorFields())); - injectFields(source, target, injectionMap); - injectMethods(source, target, injectionMap); - - return target; - } - - private function fetchConstructorArgs(source : Object, constructorFields : Array) : Array - { - const result : Array = []; - for (var i : uint = 0; i < constructorFields.length; i++) { - result.push(extractValue(source, constructorFields[i])); - } - return result; - } - - private function injectFields(source : Object, target : *, injectionMap : InjectionMap) : void - { - const fieldNames : Array = injectionMap.getFieldNames(); - for each (var fieldName : String in fieldNames) { - target[fieldName] = extractValue(source, injectionMap.getField(fieldName)); - } - } - - private function injectMethods(source : Object, target : *, injectionMap : InjectionMap) : void - { - const methodNames : Array = injectionMap.getMethodsNames(); - for each (var methodName : String in methodNames) - { - const values : Array = []; - for each (var injectionDetail : InjectionDetail in injectionMap.getMethod(methodName)) { - values.push(extractValue(source, injectionDetail)); - } - (target[methodName] as Function).apply(null, values); - } - } - - private function extractValue(source : Object, injectionDetail : InjectionDetail) : * - { - var value : * = source[injectionDetail.name]; - - // Is this a required injection? - if (injectionDetail.isRequired && value === undefined) { - throw new MarshallingError("Required value " + injectionDetail + " does not exist in the source object.", MarshallingError.MISSING_REQUIRED_FIELD); - } - - if (value) - { - // automatically coerce simple types. - if (!ObjectUtils.isSimple(value)) { - value = extract(value, injectionDetail.type); - } - - // Collections are harder, we need to coerce the contents. - else if (value is Array) { - if(isVector(injectionDetail.type)) { - value = extractVector(value, injectionDetail.type, injectionDetail.arrayTypeHint); - } - else if (injectionDetail.arrayTypeHint) { - value = extractTypedArray(value, injectionDetail.arrayTypeHint); - } - } - - // refuse to allow any automatic coercing to occur. - if (!(value is injectionDetail.type)) { - throw new MarshallingError("Could not coerce `" + injectionDetail.name + "` (value: " + value + " <" + Type.forInstance(value).clazz + "]>) from source object to " + injectionDetail.type + " on target object", MarshallingError.TYPE_MISMATCH); - } - } - - return value; - } - - private function extractTypedArray(source : Array, targetClassType : Class) : Array - { - const result : Array = new Array(source.length); - for (var i : uint = 0; i < source.length; i++) { - result[i] = extract(source[i], targetClassType); - } - return result; - } - - private function extractVector(source : Array, targetVectorClass : Class, targetClassType : Class) : * - { - const result : * = ClassUtils.newInstance(targetVectorClass); - for (var i : uint = 0; i < source.length; i++) { - if (isVector(targetClassType)) { - const type : Type = Type.forClass(targetClassType); - result[i] = extractVector(source[i], targetClassType, type.parameters[0]); - } else { - result[i] = extract(source[i], targetClassType); - } - } - return result; - } - - - private function instantiate(targetType : Class, ctorArgs : Array) : * - { - return ClassUtils.newInstance(targetType, ctorArgs); - } - - private function addReflectedRules(injectionMap : InjectionMap, targetType : Class, reflectionMap : Type) : void - { - addReflectedConstructorRules(injectionMap, reflectionMap); - addReflectedFieldRules(injectionMap, reflectionMap.fields); - addReflectedSetterRules(injectionMap, reflectionMap.methods); - } - - private function addReflectedConstructorRules(injectionMap : InjectionMap, reflectionMap : Type) : void - { - const clazzMarshallingMetadata : Array = reflectionMap.getMetadata(METADATA_TAG); - if (!clazzMarshallingMetadata) { - return; - } - - const marshallingMetadata : Metadata = clazzMarshallingMetadata[0]; - const numArgs : uint = marshallingMetadata.arguments.length; - - for (var i : uint = 0; i < numArgs; i++) { - var argument : MetadataArgument = marshallingMetadata.arguments[i]; - if (argument.key == METADATA_FIELD_KEY) { - const param : Parameter = reflectionMap.constructor.parameters[i]; - const arrayTypeHint : Class = extractArrayTypeHint(param.type); - injectionMap.addConstructorField(new InjectionDetail(argument.value, param.type.clazz, true, arrayTypeHint)); - } - } - } - - private function addReflectedFieldRules(injectionMap : InjectionMap, fields : Array) : void - { - for each (var field : Field in fields) { - if (!field.hasMetadata(Metadata.TRANSIENT) && canAccess(field)) { - const fieldMetadataEntries : Array = field.getMetadata(METADATA_TAG); - const fieldMetadata : Metadata = (fieldMetadataEntries) ? fieldMetadataEntries[0] : null; - const arrayTypeHint : Class = extractArrayTypeHint(field.type, fieldMetadata); - const sourceFieldName : String = extractFieldName(field, fieldMetadata); - - injectionMap.addField(field.name, new InjectionDetail(sourceFieldName, field.type.clazz, false, arrayTypeHint)); - } - } - } - - private function addReflectedSetterRules(injectionMap : InjectionMap, methods : Array) : void - { - for each (var method : Method in methods) { - - const methodMarshallingMetadata : Array = method.getMetadata(METADATA_TAG); - if (methodMarshallingMetadata == null) { - continue; - } - - const metadata : Metadata = methodMarshallingMetadata[0]; - const numArgs : uint = metadata.arguments.length; - - for (var i : uint = 0; i < numArgs; i++) { - var argument : MetadataArgument = metadata.arguments[i]; - if (argument.key == METADATA_FIELD_KEY) { - const param : Parameter = method.parameters[i]; - const arrayTypeHint : Class = extractArrayTypeHint(param.type, metadata); - injectionMap.addMethod(method.name, new InjectionDetail(argument.value, param.type.clazz, false, arrayTypeHint)); - } - } - } - } - - private function extractFieldName(field : Field, metadata : Metadata) : String - { - // See if a taget fieldName has been defined in the Metadata. - if (metadata) { - const numArgs : uint = metadata.arguments.length; - for (var i : uint = 0; i < numArgs; i++) { - var argument : MetadataArgument = metadata.arguments[i]; - if (argument.key == METADATA_FIELD_KEY) { - return argument.value; - } - } - } - - // Assume it's a 1 to 1 mapping. - return field.name; - } - - private function extractArrayTypeHint(type : Type, metadata : Metadata = null) : Class - { - // Vectors carry their own type hint. - if (type.parameters && type.parameters[0] is Class) { - return type.parameters[0]; - } - - // Otherwise we will look for some "type" metadata, if it was defined. - else if (metadata) { - const numArgs : uint = metadata.arguments.length; - for (var i : uint = 0; i < numArgs; i++) { - var argument : MetadataArgument = metadata.arguments[i]; - if (argument.key == METADATA_TYPE_KEY) { - const clazz : Class = ClassUtils.forName(argument.value); - return clazz; - } - } - } - - // No type hint. - return null; - } - - private function canAccess(field : Field) : Boolean - { - if (field is Variable) { - return true; - } - else if (field is Accessor) { - return (field as Accessor).writeable; - } - return false; - } - - private function isVector(obj : *) : Boolean - { - return (getQualifiedClassName(obj).indexOf('__AS3__.vec::Vector') == 0); - } - } -} diff --git a/src/org/osflash/vanilla/extract.as b/src/org/osflash/vanilla/extract.as deleted file mode 100644 index f5e0576..0000000 --- a/src/org/osflash/vanilla/extract.as +++ /dev/null @@ -1,16 +0,0 @@ -package org.osflash.vanilla -{ - /** - * Attempts to extract properties from the supplied source object into an instance of the supplied targetType. - * - * @param source Object which contains properties that you wish to transfer to a new instance of the - * supplied targetType Class. - * @param targetType The target Class of which an instance will be returned. - * @return An instance of the supplied targetType containing all the properties extracted from - * the supplied source object. - */ - public function extract(source : Object, targetType : Class) : * - { - return VANILLA_INSTANCE.extract(source, targetType); - } -} diff --git a/test-src/org/osflash/vanilla/TestExtractTypedArray.as b/src/test/as3/org/osflash/vanilla/TestExtractTypedArray.as similarity index 74% rename from test-src/org/osflash/vanilla/TestExtractTypedArray.as rename to src/test/as3/org/osflash/vanilla/TestExtractTypedArray.as index 294440d..86a42a1 100644 --- a/test-src/org/osflash/vanilla/TestExtractTypedArray.as +++ b/src/test/as3/org/osflash/vanilla/TestExtractTypedArray.as @@ -1,5 +1,6 @@ package org.osflash.vanilla { + import org.flexunit.asserts.assertEquals; import org.flexunit.asserts.assertNotNull; import org.flexunit.asserts.assertTrue; @@ -16,47 +17,62 @@ package org.osflash.vanilla [Test] public function numbers() : void { - const source : Object = {numbers:[21, 44, 1847]}; + const source : Object = {numbers : [21, 44, 1847]}; const result : NumberArrayList = extract(source, NumberArrayList); assertNotNull(result.numbers); assertEquals(3, result.numbers.length); - + assertEquals(21, result.numbers[0]); assertEquals(44, result.numbers[1]); assertEquals(1847, result.numbers[2]); } - + + + [Test] + public function toplevelStrings() : void + { + const source : Array = ["value1", "value2"]; + + const result : Array = extract(source, Array); + + assertEquals(source.length, result.length); + assertEquals(source[0], result[0]); + assertEquals(source[1], result[1]); + } + + [Test] public function strings() : void { - const source : Object = {strings:["hello", "world"]}; + const source : Object = {strings : ["hello", "world"]}; const result : StringArrayList = extract(source, StringArrayList); - + assertNotNull(result.getStrings()); assertEquals(2, result.getStrings().length); - + assertEquals("hello", result.getStrings()[0]); - assertEquals("world", result.getStrings()[1]); + assertEquals("world", result.getStrings()[1]); } - + + [Test] public function complex_viaField() : void { const source : Object = { - name: "Jonny", - addresses: [ + name : "Jonny", + addresses : [ { - address1: "Address 1", - city: "City 1" + address1 : "Address 1", + city : "City 1" }, { - address1: "Address 2", - city: "City 2" + address1 : "Address 2", + city : "City 2" } ] }; - + const result : PersonWithMultipleAddressesArrayField = extract(source, PersonWithMultipleAddressesArrayField); assertNotNull(result.addresses); assertEquals(2, result.addresses.length); diff --git a/test-src/org/osflash/vanilla/TestExtractVector.as b/src/test/as3/org/osflash/vanilla/TestExtractVector.as similarity index 64% rename from test-src/org/osflash/vanilla/TestExtractVector.as rename to src/test/as3/org/osflash/vanilla/TestExtractVector.as index a8c27d7..1c5a0b4 100644 --- a/test-src/org/osflash/vanilla/TestExtractVector.as +++ b/src/test/as3/org/osflash/vanilla/TestExtractVector.as @@ -1,60 +1,79 @@ package org.osflash.vanilla { + import org.flexunit.asserts.assertEquals; import org.flexunit.asserts.assertNotNull; import org.flexunit.asserts.assertTrue; import org.osflash.vanilla.testdata.Address; - import org.osflash.vanilla.testdata.NumberVectorList; - import org.osflash.vanilla.testdata.NestedNumberVectorList; import org.osflash.vanilla.testdata.NestedComplexVectorList; + import org.osflash.vanilla.testdata.NestedNumberVectorList; + import org.osflash.vanilla.testdata.NumberVectorList; + import org.osflash.vanilla.testdata.PersonGenderEnum; import org.osflash.vanilla.testdata.PersonWithMultipleAddressesVectorField; import org.osflash.vanilla.testdata.StringVectorList; public class TestExtractVector { + [Test] public function strings() : void { - const source : Object = { strings: ["red", "white", "blue"] }; - const result : StringVectorList = extract(source, StringVectorList); - + const source : Object = { strings : ["red", "white", "blue"] }; + const result : StringVectorList = extract(source, StringVectorList); + assertNotNull(result.strings); assertEquals(3, result.strings.length); assertEquals("red", result.strings[0]); assertEquals("white", result.strings[1]); assertEquals("blue", result.strings[2]); } - + + + [Test] + public function toplevelStrings() : void + { + const source : Array = ["value1", "value2"]; + + const result : Vector. = extract(source, Vector. as Class); + + assertEquals(source.length, result.length); + assertEquals(source[0], result[0]); + assertEquals(source[1], result[1]); + } + + [Test] public function numbers() : void { - const source : Object = { numbers: [22, 18, 10294] }; - const result : NumberVectorList = extract(source, NumberVectorList); - + const source : Object = { numbers : [22, 18, 10294] }; + const result : NumberVectorList = extract(source, NumberVectorList); + assertNotNull(result.numbers); assertEquals(3, result.numbers.length); assertEquals(22, result.numbers[0]); assertEquals(18, result.numbers[1]); assertEquals(10294, result.numbers[2]); } - + + [Test] public function complex_viaField() : void { const source : Object = { - name: "Jonny", - addresses: [ + name : "Jonny", + addresses : [ { - address1: "Address 1", - city: "City 1" + address1 : "Address 1", + city : "City 1" }, { - address1: "Address 2", - city: "City 2" + address1 : "Address 2", + city : "City 2" } - ] + ], + genders : [PersonGenderEnum.FEMALE, PersonGenderEnum.MALE] }; - + const result : PersonWithMultipleAddressesVectorField = extract(source, PersonWithMultipleAddressesVectorField); assertNotNull(result.addresses); assertEquals(2, result.addresses.length); @@ -63,13 +82,16 @@ package org.osflash.vanilla assertEquals(source["addresses"][0]["city"], result.addresses[0].city); assertEquals(source["addresses"][1]["address1"], result.addresses[1].address1); assertEquals(source["addresses"][1]["city"], result.addresses[1].city); + assertEquals(PersonGenderEnum.FEMALE, result.genders[0]); + assertEquals(PersonGenderEnum.MALE, result.genders[1]); } + [Test] public function nested_numbers() : void { const source : Object = { - numbers: [ + numbers : [ [1, 2], [4, 5], [7, 8, 9] @@ -88,33 +110,36 @@ package org.osflash.vanilla assertEquals(9, result.numbers[2][2]); } + [Test] public function nested_complex() : void { const source : Object = { - people: [ + people : [ [ { - name: "jonny", - age: 28, - artists: [ "mew", "tool" ] + name : "jonny", + age : 28, + artists : [ "mew", "tool" ] }, { - name: "mayakwd", - age: 26, - address: { - adress1: "Somewhere", - city: "Tomsk" - } + name : "mayakwd", + age : 26, + address : { + address1 : "Somewhere", + city : "Tomsk" + }, + gender : PersonGenderEnum.MALE } ] ] }; - const result: NestedComplexVectorList = extract(source, NestedComplexVectorList); + const result : NestedComplexVectorList = extract(source, NestedComplexVectorList); assertEquals("jonny", result.people[0][0].name); assertEquals("Tomsk", result.people[0][1].address.city); + assertEquals(PersonGenderEnum.MALE, result.people[0][1].gender); } } diff --git a/test-src/org/osflash/vanilla/TestVanilla.as b/src/test/as3/org/osflash/vanilla/TestVanilla.as similarity index 64% rename from test-src/org/osflash/vanilla/TestVanilla.as rename to src/test/as3/org/osflash/vanilla/TestVanilla.as index f171fc4..3683945 100644 --- a/test-src/org/osflash/vanilla/TestVanilla.as +++ b/src/test/as3/org/osflash/vanilla/TestVanilla.as @@ -1,19 +1,21 @@ package org.osflash.vanilla { + + import org.flexunit.Assert; import org.flexunit.asserts.assertEquals; import org.flexunit.asserts.assertNotNull; import org.flexunit.asserts.assertNull; - import org.flexunit.asserts.assertTrue; import org.osflash.vanilla.testdata.PersonConstructorMetadata; + import org.osflash.vanilla.testdata.PersonGenderEnum; import org.osflash.vanilla.testdata.PersonImplicitFields; import org.osflash.vanilla.testdata.PersonMutlipleArgumentSetterMetadata; import org.osflash.vanilla.testdata.PersonPublicFields; import org.osflash.vanilla.testdata.PersonPublicFieldsMetadata; import org.osflash.vanilla.testdata.PersonSetterMetadata; -import org.osflash.vanilla.testdata.PersonTransientFields; -import org.osflash.vanilla.testdata.PersonWithAddressConstructor; + import org.osflash.vanilla.testdata.PersonTransientFields; + import org.osflash.vanilla.testdata.PersonWithAddressConstructor; import org.osflash.vanilla.testdata.PersonWithAddressFields; - + /** * @author Jonny */ @@ -23,26 +25,31 @@ import org.osflash.vanilla.testdata.PersonWithAddressConstructor; * Plain old AS3 Object which we want to Marhsall into a strong type. */ public static const SOURCE : Object = { - name: "jonny", - age: 28, - artists: [ "mew", "tool" ] + name : "jonny", + age : 28, + artists : ["mew", "tool"], + gender : PersonGenderEnum.MALE }; - + private var _marshaller : Vanilla; + [Before] public function setup() : void { _marshaller = new Vanilla(); } - + + [Test] - public function noNeedToMarshall() : void { + public function noNeedToMarshall() : void + { assertEquals("string value", _marshaller.extract("string value", String)); assertEquals(42, _marshaller.extract(42, Number)); assertEquals(true, _marshaller.extract(true, Boolean)); } - + + [Test] public function withPublicFields() : void { @@ -50,8 +57,10 @@ import org.osflash.vanilla.testdata.PersonWithAddressConstructor; assertEquals(SOURCE["name"], result.name); assertEquals(SOURCE["age"], result.age); assertEquals(SOURCE["artists"], result.artists); + assertEquals(SOURCE["gender"], result.gender); } - + + [Test] public function withImplicitFields() : void { @@ -59,80 +68,87 @@ import org.osflash.vanilla.testdata.PersonWithAddressConstructor; assertEquals(SOURCE["name"], result.name); assertEquals(SOURCE["age"], result.age); assertEquals(SOURCE["artists"], result.artists); + assertEquals(SOURCE["gender"], result.gender); } - - - [Test (description="Marshalling doesn't require all fields in the TargetType to be present in the source object")] - public function missingFieldsUsingFieldInection() : void + + + [Test(description="Marshalling doesn't require all fields in the TargetType to be present in the source object")] + public function missingFieldsUsingFieldInjection() : void { - const source : Object = { name: "dave" }; + const source : Object = { name : "dave" }; const result : PersonPublicFields = _marshaller.extract(source, PersonPublicFields); assertEquals(source["name"], result.name); assertEquals(0, result.age); - assertEquals(null, result.artists); + assertNull(result.artists); } - - [Test (description="Marshalling doesn't require all fields in the TargetType to be present in the source object")] - public function missingFieldsUsingMutatorInection() : void + + + [Test(description="Marshalling doesn't require all fields in the TargetType to be present in the source object")] + public function missingFieldsUsingMutatorInjection() : void { - const source : Object = { name: "dave" }; + const source : Object = { name : "dave" }; const result : PersonSetterMetadata = _marshaller.extract(source, PersonSetterMetadata); assertEquals(source["name"], result.getName()); assertEquals(0, result.getAge()); - assertEquals(null, result.getArtists()); - } - - [Test (description="If the values dont exist in the source object, the AVM will either use the default value (if defined) or assign the default empty value for the method param's Type")] + assertNull(result.getArtists()); + } + + + [Test(description="If the values dont exist in the source object, the AVM will either use the default value (if defined) or assign the default empty value for the method param's Type")] public function missingFieldsUsingMultipleArgumentSetter() : void { - const source : Object = { name: "dave" }; + const source : Object = { name : "dave", gender : PersonGenderEnum.MALE }; const result : PersonMutlipleArgumentSetterMetadata = _marshaller.extract(source, PersonMutlipleArgumentSetterMetadata); assertEquals(source["name"], result.getName()); assertEquals(0, result.getAge()); - assertEquals(null, result.getArtists()); - } - - [Test (description="Marshalling will ignore any extra fields present in the source object that don't exist in the TargetType")] + assertEquals(source["gender"], result.getGender()); + assertNull(result.getArtists()); + } + + + [Test(description="Marshalling will ignore any extra fields present in the source object that don't exist in the TargetType")] public function withAdditionalFieldsInSource() : void { - const source : Object = { name: "bob", age: 27, artists: [ "nin", "qotsa" ], gender: "m" }; + const source : Object = { name : "bob", age : 27, artists : [ "nin", "qotsa" ], ignoredExtraField : "ignored" }; const result : PersonPublicFields = _marshaller.extract(source, PersonPublicFields); assertEquals(source["name"], result.name); assertEquals(source["age"], result.age); assertEquals(source["artists"], result.artists); - } + } + - [Test] + [Test] public function mismatchedDataType() : void { - var errorThrown : Boolean; - try { - const source : Object = { name: "Jonny", age: "27" }; + const source : Object = { name : "Jonny", age : "27" }; + + try + { _marshaller.extract(source, PersonPublicFields); - } - catch (e : MarshallingError) { - errorThrown = true; + Assert.fail("Expected MarshallingError"); + } catch (e : MarshallingError) + { assertEquals(MarshallingError.TYPE_MISMATCH, e.errorID); } - assertTrue(errorThrown); } - + + [Test] public function missingRequiredFieldDefinedByConstructorInjection() : void { - var errorThrown : Boolean; - try { - const source : Object = { name: "Jonny" }; + const source : Object = { name : "Jonny" }; + + try + { _marshaller.extract(source, PersonConstructorMetadata); - } - catch (e : MarshallingError) { - errorThrown = true; + Assert.fail("Expected MarshallingError"); + } catch (e : MarshallingError) + { assertEquals(MarshallingError.MISSING_REQUIRED_FIELD, e.errorID); } - assertTrue(errorThrown); } - - + + [Test] public function withMetadataDefinedConstructorArguments() : void { @@ -140,9 +156,10 @@ import org.osflash.vanilla.testdata.PersonWithAddressConstructor; assertEquals(SOURCE["name"], result.name); assertEquals(SOURCE["age"], result.age); assertEquals(SOURCE["artists"], result.artists); - } - - + assertEquals(SOURCE["gender"], result.gender); + } + + [Test] public function withMetadataDefinedSetters() : void { @@ -150,8 +167,10 @@ import org.osflash.vanilla.testdata.PersonWithAddressConstructor; assertEquals(SOURCE["name"], result.getName()); assertEquals(SOURCE["age"], result.getAge()); assertEquals(SOURCE["artists"], result.getArtists()); + assertEquals(SOURCE["gender"], result.getGender()); } - + + [Test] public function withMetadataDefinedMultipleArgumentSetter() : void { @@ -159,63 +178,72 @@ import org.osflash.vanilla.testdata.PersonWithAddressConstructor; assertEquals(SOURCE["name"], result.getName()); assertEquals(SOURCE["age"], result.getAge()); assertEquals(SOURCE["artists"], result.getArtists()); + assertEquals(SOURCE["gender"], result.getGender()); } - + + [Test] public function withMetadataFields() : void { const source : Object = { - myName: "Dave", - myAge: 28 + myName : "Dave", + myAge : 28, + myGender : "FEMALE" }; const result : PersonPublicFieldsMetadata = _marshaller.extract(source, PersonPublicFieldsMetadata); assertEquals(source["myName"], result.name); assertEquals(source["myAge"], result.age); + assertEquals(source["myGender"], result.gender.value); } - + + [Test] public function withNestedObjectFields() : void { const source : Object = { - name: "Jonny", - address: { - address1: "My House", - city: "London" + name : "Jonny", + address : { + address1 : "My House", + city : "London" } }; - + const result : PersonWithAddressFields = _marshaller.extract(source, PersonWithAddressFields); assertEquals(source["name"], result.name); assertNotNull(result.address); assertEquals(source["address"]["address1"], result.address.address1); - assertEquals(source["address"]["city"], result.address.city); + assertEquals(source["address"]["city"], result.address.city); } + [Test] public function withNestedObjectConstructorMetadata() : void { const source : Object = { - name: "Jonny", - address: { - address1: "My House", - city: "London" - } + name : "Jonny", + address : { + address1 : "My House", + city : "London" + }, + gender : PersonGenderEnum.MALE }; - + const result : PersonWithAddressConstructor = _marshaller.extract(source, PersonWithAddressConstructor); assertEquals(source["name"], result.name); assertNotNull(result.address); assertEquals(source["address"]["address1"], result.address.address1); - assertEquals(source["address"]["city"], result.address.city); + assertEquals(source["address"]["city"], result.address.city); + assertEquals(source["gender"], result.gender); } - [Test (description="Marshalling ignores Transient variables")] - public function transientVariablesAreIgnored() : void - { - const source : Object = { name: "dave", age:22 }; - const result : PersonTransientFields = _marshaller.extract(source, PersonTransientFields); - assertNull(result.name); - assertEquals(22, result.age); - } + + [Test(description="Marshalling ignores Transient variables")] + public function transientVariablesAreIgnored() : void + { + const source : Object = { name : "dave", age : 22 }; + const result : PersonTransientFields = _marshaller.extract(source, PersonTransientFields); + assertEquals(22, result.age); + assertNull(result.name); + } } } \ No newline at end of file diff --git a/src/test/as3/org/osflash/vanilla/caching/TestInjectionMapCache.as b/src/test/as3/org/osflash/vanilla/caching/TestInjectionMapCache.as new file mode 100644 index 0000000..4d290f5 --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/caching/TestInjectionMapCache.as @@ -0,0 +1,84 @@ +/** + * Created by sorenjepsen on 29/12/13. + */ +package org.osflash.vanilla.caching +{ + + import mockolate.mock; + import mockolate.received; + import mockolate.runner.MockolateRunner; + + import org.flexunit.assertThat; + import org.flexunit.asserts.assertEquals; + import org.hamcrest.object.instanceOf; + import org.osflash.vanilla.InjectionMap; + import org.osflash.vanilla.testdata.Address; + import org.osflash.vanilla.util.ICache; + + MockolateRunner; + + [RunWith("mockolate.runner.MockolateRunner")] + public class TestInjectionMapCache + { + + private static const TESTCLASS : Class = Address; + [Mock] + public var cacheUtil : ICache; + private var injectionMapCache : InjectionMapCache = InjectionMapCache.getInstance(); + + + [Before] + public function before() : void + { + injectionMapCache = InjectionMapCache.getInstance(); + injectionMapCache.injectionCache = cacheUtil; + } + + + [Test] + public function testGetInjectionMapForClassWhenNotCachedThenInjectionMapIsCreated() : void + { + performGetInjectionMapForClass(); + + assertThat(cacheUtil, received().method('addElement').args(TESTCLASS, instanceOf(InjectionMap)).once()); + } + + + [Test] + public function testGetInjectionMapForClassWhenCachedThenInjectionMapIsReturnedFromCache() : void + { + mockCacheUtilHasElementToReturnTrue(); + + performGetInjectionMapForClass(); + + assertThat(cacheUtil, received().method('getElement').args(TESTCLASS).once()); + } + + + [Test] + public function testGetInstanceWhenCalledAlwaysReturnsSameInstance() : void + { + assertEquals(injectionMapCache, InjectionMapCache.getInstance()); + } + + + [Test(expects="flash.errors.IllegalOperationError")] + public function testClassConstructionWhenClassIsInitializedUsingNewThenThrowException() : void + { + injectionMapCache = new InjectionMapCache(String); + } + + + private function performGetInjectionMapForClass() : void + { + injectionMapCache.getInjectionMapForClass(TESTCLASS); + } + + + private function mockCacheUtilHasElementToReturnTrue() : void + { + mock(cacheUtil).method('hasElement').args(TESTCLASS).returns(true); + } + + } +} diff --git a/test-src/org/osflash/vanilla/outside/TestExtractMethod.as b/src/test/as3/org/osflash/vanilla/outside/TestExtractMethod.as similarity index 89% rename from test-src/org/osflash/vanilla/outside/TestExtractMethod.as rename to src/test/as3/org/osflash/vanilla/outside/TestExtractMethod.as index 0113429..0387fff 100644 --- a/test-src/org/osflash/vanilla/outside/TestExtractMethod.as +++ b/src/test/as3/org/osflash/vanilla/outside/TestExtractMethod.as @@ -1,7 +1,9 @@ package org.osflash.vanilla.outside { + import org.flexunit.asserts.assertEquals; import org.osflash.vanilla.extract; + /** * @author Jonny */ @@ -11,10 +13,10 @@ package org.osflash.vanilla.outside public function useConvenienceMethod() : void { const source : Object = { - name: "Jonny", - age: 28 + name : "Jonny", + age : 28 }; - + const result : PersonVO = extract(source, PersonVO); assertEquals("Jonny", result.name); assertEquals(28, result.age); @@ -22,7 +24,8 @@ package org.osflash.vanilla.outside } } -class PersonVO { +class PersonVO +{ public var name : String; public var age : uint; } \ No newline at end of file diff --git a/test-src/org/osflash/vanilla/testdata/Address.as b/src/test/as3/org/osflash/vanilla/testdata/Address.as similarity index 99% rename from test-src/org/osflash/vanilla/testdata/Address.as rename to src/test/as3/org/osflash/vanilla/testdata/Address.as index 2d58b5e..9e9c868 100644 --- a/test-src/org/osflash/vanilla/testdata/Address.as +++ b/src/test/as3/org/osflash/vanilla/testdata/Address.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ diff --git a/test-src/org/osflash/vanilla/testdata/NestedComplexVectorList.as b/src/test/as3/org/osflash/vanilla/testdata/NestedComplexVectorList.as similarity index 99% rename from test-src/org/osflash/vanilla/testdata/NestedComplexVectorList.as rename to src/test/as3/org/osflash/vanilla/testdata/NestedComplexVectorList.as index c87af0c..7209b7e 100644 --- a/test-src/org/osflash/vanilla/testdata/NestedComplexVectorList.as +++ b/src/test/as3/org/osflash/vanilla/testdata/NestedComplexVectorList.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + public class NestedComplexVectorList { public var people : Vector.>; diff --git a/test-src/org/osflash/vanilla/testdata/NestedNumberVectorList.as b/src/test/as3/org/osflash/vanilla/testdata/NestedNumberVectorList.as similarity index 99% rename from test-src/org/osflash/vanilla/testdata/NestedNumberVectorList.as rename to src/test/as3/org/osflash/vanilla/testdata/NestedNumberVectorList.as index 37fa622..53b4c6a 100644 --- a/test-src/org/osflash/vanilla/testdata/NestedNumberVectorList.as +++ b/src/test/as3/org/osflash/vanilla/testdata/NestedNumberVectorList.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + public class NestedNumberVectorList { public var numbers : Vector.>; diff --git a/test-src/org/osflash/vanilla/testdata/NumberArrayList.as b/src/test/as3/org/osflash/vanilla/testdata/NumberArrayList.as similarity index 82% rename from test-src/org/osflash/vanilla/testdata/NumberArrayList.as rename to src/test/as3/org/osflash/vanilla/testdata/NumberArrayList.as index b9b83f8..c8b3252 100644 --- a/test-src/org/osflash/vanilla/testdata/NumberArrayList.as +++ b/src/test/as3/org/osflash/vanilla/testdata/NumberArrayList.as @@ -1,11 +1,12 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ public class NumberArrayList { - [Marshall (type="Number")] + [Marshall(type="Number")] public var numbers : Array; } } diff --git a/test-src/org/osflash/vanilla/testdata/NumberVectorList.as b/src/test/as3/org/osflash/vanilla/testdata/NumberVectorList.as similarity index 99% rename from test-src/org/osflash/vanilla/testdata/NumberVectorList.as rename to src/test/as3/org/osflash/vanilla/testdata/NumberVectorList.as index 11cdd3a..bff932a 100644 --- a/test-src/org/osflash/vanilla/testdata/NumberVectorList.as +++ b/src/test/as3/org/osflash/vanilla/testdata/NumberVectorList.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ diff --git a/test-src/org/osflash/vanilla/testdata/PersonConstructorMetadata.as b/src/test/as3/org/osflash/vanilla/testdata/PersonConstructorMetadata.as similarity index 51% rename from test-src/org/osflash/vanilla/testdata/PersonConstructorMetadata.as rename to src/test/as3/org/osflash/vanilla/testdata/PersonConstructorMetadata.as index b742753..37af44f 100644 --- a/test-src/org/osflash/vanilla/testdata/PersonConstructorMetadata.as +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonConstructorMetadata.as @@ -1,24 +1,51 @@ package org.osflash.vanilla.testdata { + /** * This imultable model requires the fields to be set in the constructor, here we define Marshalling Metadata * to define rules to map from the source object into constructor arguments. */ - [Marshall(field="name", field="age", field="artists")] + [Marshall(field="name", field="age", field="artists", field="gender")] public class PersonConstructorMetadata { + public function PersonConstructorMetadata(name : String, age : uint, artists : Array, gender : PersonGenderEnum) + { + _name = name; + _age = age; + _artists = artists; + _gender = gender; + } + + private var _name : String; + + public function get name() : String + { + return _name; + } + + private var _age : uint; + + public function get age() : uint + { + return _age; + } + + private var _artists : Array; - public function PersonConstructorMetadata(name : String, age : uint, artists : Array) { - _name = name; - _age = age; - _artists = artists; + public function get artists() : Array + { + return _artists; + } + + + private var _gender : PersonGenderEnum; + + public function get gender() : PersonGenderEnum + { + return _gender; } - - public function get name() : String { return _name; } - public function get age() : uint { return _age; } - public function get artists() : Array { return _artists; } } } diff --git a/src/test/as3/org/osflash/vanilla/testdata/PersonGenderEnum.as b/src/test/as3/org/osflash/vanilla/testdata/PersonGenderEnum.as new file mode 100644 index 0000000..1236aca --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonGenderEnum.as @@ -0,0 +1,35 @@ +/** + * Created by sorenjepsen on 08/12/13. + */ +package org.osflash.vanilla.testdata +{ + + import org.osflash.vanilla.IEnum; + + public class PersonGenderEnum implements IEnum + { + + public static const MALE : PersonGenderEnum = new PersonGenderEnum("MALE"); + public static const FEMALE : PersonGenderEnum = new PersonGenderEnum("FEMALE"); + + + public function PersonGenderEnum(gender : String) + { + this._value = gender; + } + + + private var _value : String; + + public function get value() : String + { + return _value; + } + + + public function toString() : String + { + return _value; + } + } +} diff --git a/src/test/as3/org/osflash/vanilla/testdata/PersonImplicitFields.as b/src/test/as3/org/osflash/vanilla/testdata/PersonImplicitFields.as new file mode 100644 index 0000000..0e753fb --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonImplicitFields.as @@ -0,0 +1,65 @@ +package org.osflash.vanilla.testdata +{ + + /** + * Implicit setter can be used without additional mapping rules as long as the setter's name maps to the field + * name in the source object. + */ + public class PersonImplicitFields + { + private var _name : String; + + public function get name() : String + { + return _name; + } + + + public function set name(name : String) : void + { + _name = name; + } + + + private var _age : uint; + + public function get age() : uint + { + return _age; + } + + + public function set age(age : uint) : void + { + _age = age; + } + + + private var _artists : Array; + + public function get artists() : Array + { + return _artists; + } + + + public function set artists(value : Array) : void + { + _artists = value; + } + + + private var _gender : PersonGenderEnum; + + public function get gender() : PersonGenderEnum + { + return _gender; + } + + + public function set gender(value : PersonGenderEnum) : void + { + _gender = value; + } + } +} diff --git a/src/test/as3/org/osflash/vanilla/testdata/PersonMutlipleArgumentSetterMetadata.as b/src/test/as3/org/osflash/vanilla/testdata/PersonMutlipleArgumentSetterMetadata.as new file mode 100644 index 0000000..14ee2b5 --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonMutlipleArgumentSetterMetadata.as @@ -0,0 +1,48 @@ +package org.osflash.vanilla.testdata +{ + + /** + * This class uses a single setter method to supply all the values to the object. + */ + public class PersonMutlipleArgumentSetterMetadata + { + private var _name : String; + private var _age : uint; + private var _artists : Array; + private var _gender : PersonGenderEnum; + + + [Marshall(field="name", field="age", field="artists", field="gender")] + public function init(name : String, age : uint, artists : Array, gender : PersonGenderEnum) : void + { + _name = name; + _age = age; + _artists = artists; + _gender = gender; + } + + + public function getName() : String + { + return _name; + } + + + public function getAge() : uint + { + return _age; + } + + + public function getArtists() : Array + { + return _artists; + } + + + public function getGender() : PersonGenderEnum + { + return _gender; + } + } +} diff --git a/test-src/org/osflash/vanilla/testdata/PersonPublicFields.as b/src/test/as3/org/osflash/vanilla/testdata/PersonPublicFields.as similarity index 88% rename from test-src/org/osflash/vanilla/testdata/PersonPublicFields.as rename to src/test/as3/org/osflash/vanilla/testdata/PersonPublicFields.as index b9a9035..b9b634e 100644 --- a/test-src/org/osflash/vanilla/testdata/PersonPublicFields.as +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonPublicFields.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + /** * The fields of this VO map directly to the properties of the source object; all Marshalling is automagik. */ @@ -9,5 +10,6 @@ package org.osflash.vanilla.testdata public var age : uint; public var artists : Array; public var address : Address; + public var gender : PersonGenderEnum; } } diff --git a/test-src/org/osflash/vanilla/testdata/PersonPublicFieldsMetadata.as b/src/test/as3/org/osflash/vanilla/testdata/PersonPublicFieldsMetadata.as similarity index 70% rename from test-src/org/osflash/vanilla/testdata/PersonPublicFieldsMetadata.as rename to src/test/as3/org/osflash/vanilla/testdata/PersonPublicFieldsMetadata.as index 276d97f..d10e90b 100644 --- a/test-src/org/osflash/vanilla/testdata/PersonPublicFieldsMetadata.as +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonPublicFieldsMetadata.as @@ -1,15 +1,19 @@ package org.osflash.vanilla.testdata { + /** * The fields of this VO don't map to the properties of the source object, we defined Marshalling rules in the * Metadata to create the correct mappings. */ public class PersonPublicFieldsMetadata { - [Marshall (field="myName")] + [Marshall(field="myName")] public var name : String; - - [Marshall (field="myAge")] + + [Marshall(field="myAge")] public var age : uint; + + [Marshall(field="myGender")] + public var gender : PersonGenderEnum; } } diff --git a/src/test/as3/org/osflash/vanilla/testdata/PersonSetterMetadata.as b/src/test/as3/org/osflash/vanilla/testdata/PersonSetterMetadata.as new file mode 100644 index 0000000..8f18d89 --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonSetterMetadata.as @@ -0,0 +1,67 @@ +package org.osflash.vanilla.testdata +{ + + /** + * This class uses Java style mutators; here we define the marshalling rule above each mutator as Metadata, listing + * the filed in the source object to read the value from when invoking the method. + */ + public class PersonSetterMetadata + { + private var _name : String; + private var _age : uint; + private var _artists : Array; + private var _gender : PersonGenderEnum; + + + [Marshall(field="name")] + public function setName(value : String) : void + { + _name = value; + } + + + public function getName() : String + { + return _name; + } + + + [Marshall(field="age")] + public function setAge(value : uint) : void + { + _age = value; + } + + + public function getAge() : uint + { + return _age; + } + + + [Marshall(field="artists")] + public function setArtists(value : Array) : void + { + _artists = value; + } + + + public function getArtists() : Array + { + return _artists; + } + + + [Marshall(field="gender")] + public function setGender(value : PersonGenderEnum) : void + { + _gender = value; + } + + + public function getGender() : PersonGenderEnum + { + return _gender; + } + } +} diff --git a/test-src/org/osflash/vanilla/testdata/PersonTransientFields.as b/src/test/as3/org/osflash/vanilla/testdata/PersonTransientFields.as similarity index 94% rename from test-src/org/osflash/vanilla/testdata/PersonTransientFields.as rename to src/test/as3/org/osflash/vanilla/testdata/PersonTransientFields.as index 11712ba..1c899fd 100644 --- a/test-src/org/osflash/vanilla/testdata/PersonTransientFields.as +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonTransientFields.as @@ -1,11 +1,12 @@ package org.osflash.vanilla.testdata { + /** * The fields of this VO map directly to the properties of the source object; all Marshalling is automagik. */ public class PersonTransientFields { - [Transient] + [Transient] public var name : String; public var age : uint; public var artists : Array; diff --git a/src/test/as3/org/osflash/vanilla/testdata/PersonVectorList.as b/src/test/as3/org/osflash/vanilla/testdata/PersonVectorList.as new file mode 100644 index 0000000..735c4f6 --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonVectorList.as @@ -0,0 +1,10 @@ +package org.osflash.vanilla.testdata +{ + + public class PersonVectorList + { + + public var genderList : Vector.; + + } +} diff --git a/src/test/as3/org/osflash/vanilla/testdata/PersonWithAddressConstructor.as b/src/test/as3/org/osflash/vanilla/testdata/PersonWithAddressConstructor.as new file mode 100644 index 0000000..d484cda --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonWithAddressConstructor.as @@ -0,0 +1,38 @@ +package org.osflash.vanilla.testdata +{ + + [Marshall(field="name", field="address", field="gender")] + public class PersonWithAddressConstructor + { + public function PersonWithAddressConstructor(name : String, address : Address, gender : PersonGenderEnum) + { + _name = name; + _address = address; + _gender = gender; + } + + + private var _name : String; + + public function get name() : String + { + return _name; + } + + + private var _address : Address; + + public function get address() : Address + { + return _address; + } + + + private var _gender : PersonGenderEnum; + + public function get gender() : PersonGenderEnum + { + return _gender; + } + } +} diff --git a/test-src/org/osflash/vanilla/testdata/PersonWithAddressFields.as b/src/test/as3/org/osflash/vanilla/testdata/PersonWithAddressFields.as similarity index 99% rename from test-src/org/osflash/vanilla/testdata/PersonWithAddressFields.as rename to src/test/as3/org/osflash/vanilla/testdata/PersonWithAddressFields.as index adb5bb1..13146ec 100644 --- a/test-src/org/osflash/vanilla/testdata/PersonWithAddressFields.as +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonWithAddressFields.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ diff --git a/test-src/org/osflash/vanilla/testdata/PersonWithMultipleAddressesArrayField.as b/src/test/as3/org/osflash/vanilla/testdata/PersonWithMultipleAddressesArrayField.as similarity index 75% rename from test-src/org/osflash/vanilla/testdata/PersonWithMultipleAddressesArrayField.as rename to src/test/as3/org/osflash/vanilla/testdata/PersonWithMultipleAddressesArrayField.as index f8692f9..6ba35eb 100644 --- a/test-src/org/osflash/vanilla/testdata/PersonWithMultipleAddressesArrayField.as +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonWithMultipleAddressesArrayField.as @@ -1,13 +1,14 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ public class PersonWithMultipleAddressesArrayField { public var name : String; - - [Marshall (type="org.osflash.vanilla.testdata.Address")] + + [Marshall(type="org.osflash.vanilla.testdata.Address")] public var addresses : Array; } } diff --git a/test-src/org/osflash/vanilla/testdata/PersonWithMultipleAddressesVectorField.as b/src/test/as3/org/osflash/vanilla/testdata/PersonWithMultipleAddressesVectorField.as similarity index 79% rename from test-src/org/osflash/vanilla/testdata/PersonWithMultipleAddressesVectorField.as rename to src/test/as3/org/osflash/vanilla/testdata/PersonWithMultipleAddressesVectorField.as index 57bbaf1..0548cbe 100644 --- a/test-src/org/osflash/vanilla/testdata/PersonWithMultipleAddressesVectorField.as +++ b/src/test/as3/org/osflash/vanilla/testdata/PersonWithMultipleAddressesVectorField.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ @@ -7,5 +8,6 @@ package org.osflash.vanilla.testdata { public var name : String; public var addresses : Vector.
; + public var genders : Vector.; } } diff --git a/test-src/org/osflash/vanilla/testdata/StringArrayList.as b/src/test/as3/org/osflash/vanilla/testdata/StringArrayList.as similarity index 69% rename from test-src/org/osflash/vanilla/testdata/StringArrayList.as rename to src/test/as3/org/osflash/vanilla/testdata/StringArrayList.as index 6916a96..7327818 100644 --- a/test-src/org/osflash/vanilla/testdata/StringArrayList.as +++ b/src/test/as3/org/osflash/vanilla/testdata/StringArrayList.as @@ -1,18 +1,23 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ public class StringArrayList { private var _strings : Array; - + + [Marshall(field="strings", type="String")] - public function setStrings(value : Array) : void { + public function setStrings(value : Array) : void + { _strings = value; } - - public function getStrings() : Array { + + + public function getStrings() : Array + { return _strings; } } diff --git a/test-src/org/osflash/vanilla/testdata/StringVectorList.as b/src/test/as3/org/osflash/vanilla/testdata/StringVectorList.as similarity index 99% rename from test-src/org/osflash/vanilla/testdata/StringVectorList.as rename to src/test/as3/org/osflash/vanilla/testdata/StringVectorList.as index e49e0ed..3612092 100644 --- a/test-src/org/osflash/vanilla/testdata/StringVectorList.as +++ b/src/test/as3/org/osflash/vanilla/testdata/StringVectorList.as @@ -1,5 +1,6 @@ package org.osflash.vanilla.testdata { + /** * @author Jonny */ diff --git a/src/test/as3/org/osflash/vanilla/util/TestCacheUtil.as b/src/test/as3/org/osflash/vanilla/util/TestCacheUtil.as new file mode 100644 index 0000000..d81a6b7 --- /dev/null +++ b/src/test/as3/org/osflash/vanilla/util/TestCacheUtil.as @@ -0,0 +1,75 @@ +package org.osflash.vanilla.util +{ + + import org.flexunit.asserts.assertEquals; + import org.flexunit.asserts.assertFalse; + import org.flexunit.asserts.assertTrue; + + public class TestCacheUtil + { + + private static const KEY : String = "cache_key"; + private static const VALUE : Object = {value : "Some value"}; + private var cacheUtil : CacheUtil; + + + [Before] + public function before() : void + { + cacheUtil = new CacheUtil(); + } + + + [Test] + public function testAddElementToCacheWhenAddedThenCacheExists() : void + { + performAddElement(); + + assertEquals(VALUE, cacheUtil.getElement(KEY)); + } + + + [Test] + public function testHasElementWhenElementIsCachedThenReturnsTrue() : void + { + performAddElement(); + + assertTrue(cacheUtil.hasElement(KEY)); + } + + + [Test] + public function testHasElementWhenElementIsNotCachedThenReturnsFalse() : void + { + assertFalse(cacheUtil.hasElement(KEY)); + } + + + [Test] + public function testGetElementWhenElementIsCachedThenReturnsElement() : void + { + performAddElement(); + + assertEquals(VALUE, performGetElement()); + } + + + [Test(expects="org.as3commons.lang.IllegalArgumentError")] + public function testGetElementWhenElementIsNotCachedThenThrowsException() : void + { + performGetElement(); + } + + + private function performAddElement() : void + { + cacheUtil.addElement(KEY, VALUE); + } + + + private function performGetElement() : Object + { + return cacheUtil.getElement(KEY); + } + } +} diff --git a/test-src/TestRunner.as b/test-src/TestRunner.as deleted file mode 100644 index 9505839..0000000 --- a/test-src/TestRunner.as +++ /dev/null @@ -1,42 +0,0 @@ -package -{ - import org.osflash.vanilla.TestExtractTypedArray; - import org.flexunit.internals.TraceListener; - import org.flexunit.listeners.CIListener; - import org.flexunit.runner.FlexUnitCore; - import org.osflash.vanilla.TestExtractVector; - import org.osflash.vanilla.TestVanilla; - import org.osflash.vanilla.outside.TestExtractMethod; - - import flash.display.Sprite; - import flash.display.Stage; - import flash.events.Event; - - public class TestRunner extends Sprite - { - public static var STAGE : Stage; - - private var core : FlexUnitCore; - - public function TestRunner() - { - addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); - } - - private function onAddedToStage(event : Event) : void - { - STAGE = stage; - - core = new FlexUnitCore(); - core.addListener(new TraceListener()); - core.addListener(new CIListener()); - - core.run([ - TestVanilla, - TestExtractMethod, - TestExtractVector, - TestExtractTypedArray - ]); - } - } -} diff --git a/test-src/org/osflash/vanilla/testdata/PersonImplicitFields.as b/test-src/org/osflash/vanilla/testdata/PersonImplicitFields.as deleted file mode 100644 index 5353008..0000000 --- a/test-src/org/osflash/vanilla/testdata/PersonImplicitFields.as +++ /dev/null @@ -1,22 +0,0 @@ -package org.osflash.vanilla.testdata -{ - /** - * Implicit setter can be used without additional mapping rules as long as the setter's name maps to the field - * name in the source object. - */ - public class PersonImplicitFields - { - private var _name : String; - private var _age : uint; - private var _artists : Array; - - public function get name() : String { return _name; } - public function set name(name : String) : void { _name = name; } - - public function get age() : uint { return _age; } - public function set age(age : uint) : void { _age = age; } - - public function get artists() : Array { return _artists; } - public function set artists(value : Array) : void { _artists = value; } - } -} diff --git a/test-src/org/osflash/vanilla/testdata/PersonMutlipleArgumentSetterMetadata.as b/test-src/org/osflash/vanilla/testdata/PersonMutlipleArgumentSetterMetadata.as deleted file mode 100644 index fee9da3..0000000 --- a/test-src/org/osflash/vanilla/testdata/PersonMutlipleArgumentSetterMetadata.as +++ /dev/null @@ -1,24 +0,0 @@ -package org.osflash.vanilla.testdata -{ - /** - * This class uses a single setter method to supply all the values to the object. - */ - public class PersonMutlipleArgumentSetterMetadata - { - private var _name : String; - private var _age : uint; - private var _artists : Array; - - [Marshall(field="name", field="age", field="artists")] - public function init(name : String, age : uint, artists : Array) : void - { - _name = name; - _age = age; - _artists = artists; - } - - public function getName() : String { return _name; } - public function getAge() : uint { return _age; } - public function getArtists() : Array { return _artists; } - } -} diff --git a/test-src/org/osflash/vanilla/testdata/PersonSetterMetadata.as b/test-src/org/osflash/vanilla/testdata/PersonSetterMetadata.as deleted file mode 100644 index 6893858..0000000 --- a/test-src/org/osflash/vanilla/testdata/PersonSetterMetadata.as +++ /dev/null @@ -1,26 +0,0 @@ -package org.osflash.vanilla.testdata -{ - /** - * This class uses Java style mutators; here we define the marshalling rule above each mutator as Metadata, listing - * the filed in the source object to read the value from when invoking the method. - */ - public class PersonSetterMetadata - { - private var _name : String; - private var _age : uint; - private var _artists : Array; - - [Marshall(field="name")] - public function setName(value : String) : void { _name = value; } - public function getName() : String { return _name; } - - [Marshall(field="age")] - public function setAge(value : uint) : void { _age = value; } - public function getAge() : uint { return _age; } - - [Marshall(field="artists")] - public function setArtists(value : Array) : void { _artists = value; } - public function getArtists() : Array { return _artists; } - - } -} diff --git a/test-src/org/osflash/vanilla/testdata/PersonWithAddressConstructor.as b/test-src/org/osflash/vanilla/testdata/PersonWithAddressConstructor.as deleted file mode 100644 index a4c8d9e..0000000 --- a/test-src/org/osflash/vanilla/testdata/PersonWithAddressConstructor.as +++ /dev/null @@ -1,22 +0,0 @@ -package org.osflash.vanilla.testdata -{ - [Marshall(field="name", field="address")] - public class PersonWithAddressConstructor - { - private var _name : String; - private var _address : Address; - - public function PersonWithAddressConstructor(name : String, address : Address) { - _name = name; - _address = address; - } - - public function get name() : String { - return _name; - } - - public function get address() : Address { - return _address; - } - } -}