Reading all classes under a package or reading classes with same Metadata in Actionscript 3.0 - actionscript-3

I am doing an Actionscript 3.0 project which involves introspection. I am wondering if there is a way to get all the classes within a given package structure.
For e.g. Say there are three as3 classes:
com.example.test.classOne
com.example.test.classTwo
com.example.test.classThree
I want to be able to say
getClassesUnderPackageName("com.example.test");
and get back
"com.example.test::classOne"
"com.example.test::classTwo"
"com.example.test::classThree".
Is there a way to do that?
If this is not possible, is there a way to read classes which have the same metadata?
E.g. If all the mentioned classes have the same metadata [MetadataName(type="example")] defined, is there a way to say
getClassesWithSameMetadata("MetadataName");
and get back
"com.example.test::classOne"
"com.example.test::classTwo"
"com.example.test::classThree".
Thank you.

you can use flash.utils.describeType to return XML data containing this information. it works differently on base classes, like flash.display.Sprite, but for custom classes/directories, you can write something like this:
package branchA.branchB.branchC
{
//Imports
import flash.utils.describeType;
//Class
public class Test
{
//Constructor
public function Test()
{
trace(describeType(this).#name);
}
}
}
//OUTPUT: branchA.branchB.branchC::Test
if you wanted to return the base class, you could write something like this:
package
{
//Imports
import flash.display.Sprite;
import flash.utils.describeType;
//Class
public class Test extends Sprite
{
//Constructor
public function Test()
{
trace(describeType(this).#base);
}
}
}
//OUTPUT: flash.display::Sprite
there is lots of other useful information you can get by parsing the returned XML data of describeType.
Update:
class objects do not need to have been instantiated first in order to retrieve their information via describeType(). you could build a public static function (or whatever) which accepts an array of your class objects and returns an array of strings containing the required describeType data.
something like this:
package
{
import flash.utils.describeType;
final public class Describe
{
public static function packageNames(classObjects:Array):Vector.<String>
{
var names:Vector.<String> = new Vector.<String>();
for each (var classObject in classObjects)
names.push(describeType(classObject.#name.toString()));
return names;
}
}
}
then from anywhere in your program, you can pass an array of all the classes like this:
var names:Vector.<String> = Describe.packageNames(new Array(classOne, classTwo, classThree));
trace(names);
//Output:
//com.example.test::classOne
//com.example.test::classTwo
//com.example.test::classThree

There's no inbuilt mechanism for finding classes without already knowing the class name. :(
However if you load in a SWF as a ByteArray then it's possible to iterate through the classes in it.
This might be overkill for what you want.
http://www.bytearray.org/?p=175

Take a look at AS3 Commons Bytecode. It allows you to do Bytecode based reflection. You can list all classes (you'll need to filter those if you just want a particular package), list classes with certain metadata or classes that implement a certain interface.

Related

Call to a possibly undefined method '' through a reference with static type Class

I made small .fla file in Flash Professional, and I have added .as (ActionScript File) in Flash Professional, and I have added something like code below to .as (ActionScript file), but the error appears and I am trying to figure it out, but can't, so I decided to post it in here instead.
package
{
import flash.display.MovieClip;
public class Bag extends MovieClip
{
static var firstBag:String;
public static function set setFirstBag(value:String):void
{
firstBag = value;
}
public static function get getFirstBag():String
{
return firstBag;
}
}
}
and I called it like this:
button1.addEventListener(MouseEvent.CLICK, onClickFirstButton);
function onClickFirstButton(e:MouseEvent):void
{
Bag.setFirstBag("First slot in the bag has been filled up!");
}
But I have received this following error:
Call to a possibly undefined method setFirstBag through a reference
with static type Class.
What could I do wrong?
The .as file and .fla file are on the same folder.
if I changed the Bag class to static. The error will be like this:
The static attribute may be used only on definitions inside a class.
Your answer much appreciated!
Thank you!
You're useing get like it is a mettod, but thay are accessors for properties so intead of:
Bag.setFirstBag("First slot in the bag has been filled up!");
use
Bag.setFirstBag ="First slot in the bag has been filled up!";
A few additional thoughts...
While syntactically valid, the definition and naming of your getter and setter is confusing and atypical, which I think contributed to your confusion about the behavior. You've actually defined two separate properties, one is write-only ("setFirstBag") and one is read-only ("getFirstBag"). Usually you define a getter/setter as the same property (ex "firstBag"), and without any "get" or "set" in the property name, since that is what the getter/setter is defining for you. Example:
private static var _firstBag:String;
public static function get firstBag():String {
return _firstBag:
}
public static function set firstBag(value:String):void {
_firstBag = value;
}
// usage
Bag.firstBag = "stuff";
trace(Bag.firstBag); // "stuff"
Also, you may very well have a good reason to use a getter/setter here, or you might just prefer it, but from the code you posted you could just define a public static var to do the same thing. (If you did, refactoring into a getter/setter if you needed some side-effect logic would be trivial, since the public API remains the same.)

How Actionscript 3 Classes Work

I need a little help understanding how classes work in Actionscript 3. I understand you start with "package" and why and then go to import any necessary libraries, as well as then naming the class and stating if it's public/private and extends anything.
After that is what I don't understand. It seems you write "(public) function class name()
I don't understand why you do this and what goes in the curly brackets.
I've probably missed a bit of earlier reading because I've done a little reading but I can't seem to get it.
Could someone try explain it to me? Thanks.
ActionScript 3 Classes
The package statement.
Okay, so firstly like you mentioned, a class must be wrapped by a package1. This gives us the first block, where you need to define the class.
package
{
// Your class here.
}
The package statement reflects the location of the class relative to the .fla2. For example, if you have a folder "classes" within the same directory as the project .fla, then classes within that folder will need a package statement that reflects that:
package classes
{
// Your class here.
}
Defining the class.
Within a package statement, you may insert one class. Do not confuse this with the package itself, which can contain many classes - each class just needs to have its own file with the same package statement.
A class definition is made up of up to 5 parts:
The namespace. A class can be internal or public. An internal class can only be seen by classes within the same package, whereas public classes can be seen from anywhere in the project.
The class name.
A base class (optional). If a base class is defined, then your new class will act as an extension to that class, inheriting all of the qualities of the base class.
An interface to implement (optional). Interfaces are an advanced topic thus I suggest you forget about these for now until your AS3 and OOP have evolved.
If you wanted to create a class called "Person" within the package classes, then we would end up with:
package classes
{
public class Person
{
// Class qualities here.
}
}
Properties.
Classes can contain properties. Properties are defined using the var keyword. They may belong to one of a number of namespaces (including your own) and are used to hold values that belong to your class. Properties are most commonly found clustered together at the top of your class.
Our Person class may enjoy the properties height and weight:
package classes
{
public class Person
{
// Properties.
public var height:Number = 1.70;
public var weight:Number = 67.5;
}
}
These properties can be accessed via any instance of Person that you create. Each instance will have its own set of these properties.
Class constructors (I believe this is what you're asking about).
Constructors are used to hold logic that should be run as soon as an instance of your class is created. The class constructor has the same name as the class itself. It must be public and it does not return anything. Constructors can accept arguments, which are typically used to pass in references to dependencies for that class or required values.
package classes
{
public class Person
{
// Properties.
public var height:Number = 1.70;
public var weight:Number = 67.5;
// Constructor.
public function Person(height:Number, weight:Number)
{
this.height = height;
this.weight = weight;
}
}
}
Methods.
Methods are used to hold logic that can be run when calling that method. Methods often return values and can accept arguments. Methods can belong to any namespace that you would expect properties to be able to belong to.
We may want to be able to easily determine the BMI of each instance of Person that we create, so we should create a method for that:
package classes
{
public class Person
{
// Properties.
public var height:Number = 170;
public var weight:Number = 65.5;
// Constructor.
public function Person(height:Number, weight:Number)
{
this.height = height;
this.weight = weight;
}
// Determine my BMI and return the result.
public function getBMI():Number
{
return weight / (height * height);
}
}
}
Instances.
Now that we've defined our new class, we can create instances of this class using the new keyword. This can be done from anywhere that can access the Person class, which in this case is anywhere in the project because we've made the class public.
Though the class is public, accessing it from anywhere outside of the package it belongs in will require the use of an import statement. This statement will need to be used within any class that belongs to a different package. The import statement follows the same name used for the package and includes the name of the class you want to include on the end:
import classes.Person;
Once you've imported Person, you can create instances of it and assign them to a variable with different height and weight values:
var marty:Person = new Person(71, 1.76);
var bruce:Person = new Person(96.4, 1.72);
We can then obtain the BMI for each person using their getBMI() method:
trace(marty.getBMI()); // 22.9
trace(bruce.getBMI()); // 32.6
1 You can place classes outside of a package which can be referred to in the same .as file.
2 You can add more source paths, and packages can be relative to that.
The function that have the same name as class is a constructor. In curly brackets is basically part of code that will execute instantly when object will be created. Try to search info about constructors, they exist I think in every object oriented programming language (I may be wrong), so you have a lot of resources.
You can also read about this concept on Wikipedia.
The function that is named the same as the class is the constructor. It's optional, so you can leave it out if you don't need it. A default constructor will be added, which essentially does nothing.
The constructor lets you write code that executes immediately after an instance of the class is created (ie when another bit of code runs new ClassName(). You would typically use it to initialise some variables that are used by the class. Defining a constructor also lets you handle constructor arguments, which other code can pass when they use the new operator.

Alter ResourceManager to split values by semicolon, not comma

As the title says, is there a way to alter the ResourceManager's getStringArray() in a way that it splits the resources by semicolon, not comma?
The actual method can be found in the ResourceManagerImpl class, which can be found in in the package mx.resources.
Overriding that method would be fine, but ideally I'd like to write my own getStringArray with a variable separator, however, there seems to be no way of extending either the ResourceManager or ResourceManagerImpl class to somehow add that method.
Anyone got a clue what to do here?
The problem is not that you can't extend ResourceManagerImpl since it's not final, but rather that you have to be able to register your implementation with the application instead of the default one. And doing this is a bit tricky.
So first create your implementation:
public class MyResourceManager extends ResourceManagerImpl {
private static var instance:IResourceManager;
static public function getInstance():IResourceManager
{
if (!instance) instance = new MyResourceManager();
return instance;
}
override public function getStringArray(bundleName:String,
resourceName:String,
locale:String = null):Array {
//do your stuff
}
}
So we've overriden the getStringArray method. Notice that we've done the same for getInstance, because we want it to return a new instance of MyResourceManager instead of ResourceManagerImpl (we don't have to mark override because it's a static method). Also, you may have to write some import statements manually, because some of the classes you're using are marked as 'excluded'.
Now we have to tell Flex to use MyResourceManager instead of ResourceManagerImpl. We can do this with the following code:
import mx.core.Singleton;
Singleton.registerClass("mx.resources::IResourceManager", MyResourceManager);
The problem is that we have to do this before Flex registers ResourceManagerImpl, because you can't override it once it's registered. For this we need to create a custom preloader in which we do the registering (sadly, the Application's 'preinitialize' phase is not early enough).
public class RegisteringPreloader extends DownloadProgressBar {
override public function initialize():void {
super.initialize();
Singleton.registerClass("mx.resources::IResourceManager",
MyResourceManager);
}
}
Now assign the custom preloader to the application and we're done:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
preloader="RegisteringPreloader" >
For further info I refer you to a fairly similar, but somewhat more elaborate answer that I wrote for a different question: Is there a way to listen for events on the pop up manager class?
Just for the record: if you want to provide your localization with array of strings containing commas, it is way easier to use getObject method of IResourceManager.
In your properties file:
my.beloved.strings: ["That's it, string one", "Okay, string two"]
In your code:
var strings:Array = _resourceManager.getObject(_bundleId, 'my.beloved.strings') as Array;
var stringOne:String = strings[0];
You don't have to override anything this way.

Robotlegs wiring up dependencies that belong to a base class

I'm using robot legs, I've got a bunch of ServiceResponses that extends a base class and have a dependency on a Parser, IParser. I need to wire in a parser specific to the subclass. Here's an example:
ModuleConfigResponse extends SimpleServiceResponse and implements IServiceResponse.
The initial part is easy to wire in the context, here's an example:
injector.mapClass(IServiceResponse, ModuleConfigResponse);
injector.mapClass(IServiceResponse, SimpleServiceResponse, "roomconfig");
..etc
Each Response uses a parser that is used by the baseclass:
injector.mapValue(IParser, ModuleConfigParser, "moduleconfig");
injector.mapValue(IParser, RoomConfigParser, "roomconfig");
The question is how to tie these together. The base class could have:
[Inject]
public var parser : IParser
But I can't define the type ahead of time. Im wondering if there a nice way of wiring this in the context. For the moment I've decided to wire this up by instanciating responses in a ResponseFactory instead so that I pay pass the parser manually in the constructor.
injector.mapValue(IParser, ModuleConfigParser, "moduleconfig");
I realised that not everything can be mapped in the context, RL trapped me into this way of thinking. But I've realised that its far better to map a factory to produce these objects which have very specific dependencies, than littler the codebase with marker interfaces or strings :)
one solution is to have the following in your base class:
protected var _parser : IParser
Then for instance in ModuleConfigResponse
[Inject(name='moduleconfig')]
public function set parser( value : IParser ) : void{
_parser = value;
}
But TBH, using named injections is STRONGLY discouraged, you might as well use a marker interface:
public interface IModuleConfigParser extends IParser{}
the base class stays the same, but ModuleConfigResponse would then use:
[Inject]
public function set parser( value : IModuleConfigParser ) : void{
_parser = value;
}

making a static/class function outside class

how to make a class function with actionscript, I need to have a few static tool functions that are easily used from other classes like
test = tools.rtrim(xx);
e.g. this does not compile:
package com.my.tools
{
static function rtrim(string:String):String {
return string.replace(/\s+$/,"");
}
}
It needs to be attached to a class type not a package
Try
package com.my.tools
{
public class Tools
{
public static function rtrim(string:String):String
{
return string.replace(/\s+$/,"");
}
}
}
You can then use it through Tools.rtrim("yourString");
In case your collection of tools gets big it may be useful to use top-level functions as well.
Specially if you want to reuse a tiny selection of your "tools" in other projects without wasting file size by compiling the unused ones (which happens if you include them all in the same class).
In order to do so, in your package folder you will have to create one file per function. Each file should be named the same way as its related function.
e.g. the content of each file named rtrim.as would look like this :
package com.my.tools {
public function rtrim(str:String) : String {
return string.replace(/\s+$/,"");
}
}
Then you will just have to import the top-level function where you need it :
package my {
import com.my.tools.rtrim;
public class Test
{
rtrim("bla bla");
}
}