So, I have successfully grabbed a value out of an XML document and set it into a separate class called "AddCommas." The trace functions have shown me that it sets properly.
For more details, my objective is to take the language indicator ("fr" for french or "en" for english), set it inside the appropriate class and into a variable I will use. Now, I am using this variable to be used in an if statement; which will help me format a number properly (commas, decimals, spaces) per the clients request.
However, my problem is when I try to get the value to use it. It always comes back as Null. I have placed traces all over my program trying to pinpoint when this happens, but I cannot find it. Here's the code...
The pull from the XML file and into the set (this works fine, but I am adding it for your benefit in case I missed something)
public var commaHold = new AddCommas();
localLanguage = xmlObj.localLanguage;
trace("localLanguage + " + localLanguage);
commaHold.setLanguage(localLanguage); // Set Language
//More code follows...
This is the set function istelf...
public function setLanguage(localLanguage:String){
langHold = localLanguage;
trace("Set Language = " + langHold); //This always shows a successful set
}
Now am I wrong in thinking that in AS3, once langHold in my AddCommas class has been set I should be able to use it without calling a get within the function I am using the If Statement in, right? Such as this?
var language = langHold;
if (language == "en"){
trace("Language is = " + language); // More code follows afterwards and as of now, this shows NULL
Now, I have attempted plenty of Get functions to add the language variable in the call itself to this function and it's always the same. Am I missing some fundamentals here?
Thank you very much for your time.
If you expect a string comparison you need to use quotes, unless en is a String variable since langHold is a String, like:
if (language == "en"){
Consider modifying the set function to use the as3 keyword like:
private var _language:String;
public function set language(value:String):void {
_language = value;
//do other stuff here if necessary, put a breakpoint on the line above
}
public function get language():String{
return _language;
//put a breakpoint on the line above
}
You should be able to see when any instance of your class has the property changed. The only other issue I can think of is it is not the same instance of the class and therefore doesn't share the property value you set earlier. In the debugger you can check the "hashCode" or "address" it shows for this to see if it changes when it hits the breakpoints.
Here's a sample Singleton structure in AS3 (this all goes in one file):
package com.shaunhusain.singletonExample
{
public class SingletonExample
{
private static var instance:SingletonExample;
public static function getIntance():SingletonExample
{
if( instance == null ) instance = new SingletonExample( new SingletonEnforcer() );
return instance;
}
/**
*
* #param se Blocks creation of new managers instead use static method getInstance
*/
public function SingletonExample(se:SingletonEnforcer)
{
}
}
}
internal class SingletonEnforcer {public function SingletonEnforcer(){}}
using this single shared instance from any other class would look something like this:
private var singletonInstance:SingletonExample = SingletonExample.getInstance();
ShaunHusain's theory of using a Singleton was the perfect solution I needed. However, his code gave me a bizarre 1061 error and my format and code appeared to be error free. Regardless, I looked up another way to use a Singleton as follows that worked perfectly for me. Honestly, Shaun's code should work for anyone and I have no idea why it wasn't. I am perfectly willing to admit that it was probably a typo on my end that I just did not see.
I ended up embedding the Set and Get within the Singletons class and used it as an intermediary to hold the information I needed. It worked perfectly.
package chart {
import chart.*;
//
public class StaticInstance {
private static var instance:StaticInstance;
private static var allowInstantiation:Boolean;
private var language:String;
public static function getInstance():StaticInstance {
if (instance == null) {
allowInstantiation = true;
instance = new StaticInstance();
allowInstantiation = false;
}
return instance;
}
public function StaticInstance():void {
if (!allowInstantiation) {
throw new Error("Error: Instantiation failed: Use StaticInsance.getInstance() instead of new.");
}
}
public function setLanguage(_language:String):void{
language = _language;
trace("language set = " + language);
}
public function getLanguage():String{
return language;
}
}
}
This code allowed me to hold the data and call upon it again from two different classes. It's a very hack job instead of just being able to pass on the variable from function to function, but in my case we didn't create this file, we are modifying it and attempting to do things beyond the original scope of the project.
Thanks again for your help Shaun! I hope this helps other people!
Related
My current project is in as3, but this is something I am curious about for other languages as well.
I'm attempting to use a factory object to create the appropriate object dynamically. My LevelFactory has a static method that returns a new instance of the level number provided to the method. In the code calling that method, I am able to dynamically create the buttons to call the levels like so:
for (var i:int = 1; i < 4; i++) {
var tempbutton:Sprite = createButton("Level " + i, 25, 25 +(60 * i), start(i));
_buttons.push(button);
}
This code just creates a simple button with the given arguments (ButtonText, x, y, function). It's working fine. The buttons are created, and clicking on one of them calls this method with the appropriate argument
private function start(level:int):Function {
return function(e:MouseEvent):void {
disableButtons();
newLevel = LevelFactory.createLevel(level);
addChild(newLevel);
}
}
This is all working fine; I'm just providing it for background context. The question I have is this: Is it possible to dynamically choose the type of object that my static function returns? Currently, I have am doing it as follows
public static function createLevel(level:int):Level {
var result:Level;
switch(level) {
case 1: result = new Level1(); break;
case 2: result = new Level2(); break;
//etc
}
return result;
}
I should note that all of these Level1, Level2, etc. classes extend my base level class. (Yay polymorphism!) What I would like to do is be able to do something along the lines of
public static function createLevel(level:int):Level {
var result:Level;
var levelType:String = "Level" + level;
return new levelType();
}
Obviously it's not going to work with a string like that, but is there any way to accomplish this in as3? What about other languages, such as Java or Python? Can you dynamically choose what type of child class to instantiate?
Update:
import Levels.*;
import flash.events.*;
import flash.utils.*;
public class LevelFactory
{
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.' + 'Level' + level) as Class;
var result:Level = new ref();
return result;
}
}
Update/Edit: getDefinitionByName seems to be what I'm looking for, but it has a problem. It seems that the compiler will strip unused imports, which means that unless I declare each subclass in the code ahead of time, this method will get a reference error. How can I get around the need to declare each class separately (which defeats the purpose of dynamic instantiation)?
Yes, you sure can, and it's very similar to the string thing that you've provided. The only thing that you are missing is the getDefinitionByName method: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/package.html#getDefinitionByName()
You can generate whatever class name you want, and what this method does is that it searches for that class in it's namespace, and if it finds it - it returns it as a class:
var ClassReference:Class = getDefinitionByName("flash.display.Sprite") as Class;
var instance:Object = new ClassReference();
This piece of code will instantiate a Sprite. This way you can instantiate your classes without all those switches and cases, especially when you have to make a hundred levels :)
Hope that helps! Cheers!
Edit:
In your case, the code should be:
var ref:Class = getDefinitionByName('com.path.Level' + index) as Class;
var level:Level = new ref(); // it will actually be Level1 Class
Since Andrey didn't quite finish helping me out, I am writing up a more complete answer to the question after much research.
getDefinitionByName definitely has the use I am looking for. However, unlike its use in Java, you HAVE to have a hard reference to the class you want instantiated somewhere in your code. Merely imported the class is not enough; the reason for this is that the compiler will strip the reference from any unused import to save space. So if you import the package of classes you want to choose dynamically but don't have a hard reference to them, the compiler will de-reference them. This will lead to a run-time error when the program cannot find the appropriate reference to your class.
Note that you don't actually have to do anything with the reference. You just have to declare a reference so that it can be found at run-time. So the following code will work to eliminate the switch-case statement and allow me to dynamically declare which class I am using at run-time.
{
import Levels.*;
import flash.events.*;
import flash.utils.*;
/**
*
* Returns the requested level using the createLevel class
* ...
* #author Joshua Zollinger
*/
public class LevelFactory
{
Level1, Level2, Level3, Level4, Level5, Level6, Level7;
public static function createLevel(level:int):Level {
var ref:Class = getDefinitionByName('Levels.Level' + level) as Class;
var result:Level = new ref(); // it will actually be the correct class
return result;
}}}
The obvious downside to this is that you still have to have a hard-coded reference to every class that can be instantiated like this. In this case, if I try to create a Level8 instance, it will through a run-time error because Level8 is not referenced. So every time I create a new level, I still have to go add a reference to it; I can't just use the reference dynamically.
There are supposedly ways around this that I have not tested yet, such as putting the code for the classes in a separate SWF and importing the SWF at run-time or using outside libraries that will have different functionality. If anyone has a solid way to get a truly dynamic reference that doesn't require a hard coded reference anywhere, I would love to hear about it.
Of course, it's still a lot cleaner this way; I don't have a extensive switch case statement to pack all the levels. And it's easier and faster to add a reference to the list than creating a new case in a switch. Plus it is closer to dynamic programming, which is usually a good thing.
I'm trying to iterate through the properties of a custom class, however, the methods provided by Adobe appear to not work. I'm not receiving compile or run-time errors.
Class
package {
public dynamic class enum {
public var foo:Number = 123;
public function enum() {
this.setPropertyIsEnumerable("foo", true);
if (this.propertyIsEnumerable("foo") == false) {
trace("foo:" + foo + " is not enumerable.")
}
}
}
}
// outputs "foo:bar is not enumerable."
Implementaiton
var test:enum = new enum();
for (var property:String in test) {
trace(property);
}
// outputs nothing
I try to keep my code fast and flexible, so it's really frustrating when you must change the class to Dynamic just to be able to use for ... in on the properties. Jackson Dunstan's testing confirms that this can be 400x slower than static class properties, but those have to be explicitly referenced (impractical for property agnostic methods), or use reflection of the class (computationally expensive) to be accessible.
The only way I've found to sidestep the whole issue is to use dynamically declared variables... which is pointless since at that point using setPropertyIsEnumerable(prop, true) is superfluous; all dynamically created properties already are enumerable. Additionally, dynamic variables cannot be strongly datatyped, and performance goes out the window.
For example...
Class
package {
public dynamic class enum {
public var foo:String = "apple";
public function enum(){
this.dynamicVar = "orange";
this.dynamicProp = "banana";
this.setPropertyIsEnumerable("foo", true);
this.setPropertyIsEnumerable("dynamicProp", false);
}
}
}
Implementation
var test:enum = new enum();
for (var key:String in test) {
trace(key + ": " + test[key]); // dynamicVar: 1
}
// outputs "dynamicVar: orange"
Now that the class is dynamic, we see that only one of our 3 test properties are being iterated. There should be 2.
It almost feels like Adobe wants us to adopt bad programming habits. Words fail me...
Non-dynamic classes do not provide enumerable properties or methods.
As stated in the description of the link you provided.
Sets the availability of a dynamic property for loop operations.
I think you might want to refactor your code on this approach.
I have never had to loop over a classes properties like you are doing here.
If you want to track items dynamically you should use an associative array and track them that way not at the class level like you are doing.
And if you want strong data typing then use a vector.
I am currently working with some code that my co-worker wrote. Here is a simplified look at it:
The People class:
package model{
public class People extends Array{ // NOTE: it is not dynamic
public function toXML():XML {
var out:XML = <people/>;
for each(var per:Person in this){
out.appendChild(per.toXML());
}
return out;
}
}
}
Which is basicly an Array of Persons:
package model{
public class Person {
public var name:String;
public var phoneNumber:String;
public function Person(name:String, phoneNumber:String){
this.name = name;
this.phoneNumber = phoneNumber;
}
public function toXML():XML {
var xml:XML = <person/>;
xml.#name = name;
xml.#phone = phoneNumber;
return xml;
}
}
}
This is basicly how my co-worker is using the code:
var people:People = new People();
people.push(new Person("Jake", "902 825-4444"));
people.push(new Person("Bob", "514 444-3333"));
return people.toXML().toXMLString();
Note: The he adds Person objects but he never looks at what is in the People Array except to print out the XML
Fast-forward (do people still know that this means?) to the present. I now need to look inside the People class and do something like this:
var people:People = ... init and fill with Person objects
for(var i:int=0; i<people.length(); i++){
doSomething(people[i]); // <-- Error thrown here.
}
Unfortionatly this throws this error:
ReferenceError: Error #1069: Property 0 not found on model.People and there is no default value.
at runner::Runner$/newUse()
at ExtendsArray/start()
at ExtendsArray/___ExtendsArray_Application1_initialize()
at flash.events::EventDispatcher/dispatchEventFunction()
What should I do?
Edit, Aside: Extending Array is not my doing this is part of our old model. We are moving away from the old model because it is full of garbage like this. I just need to grab this stuff from the old model to convert it into the new model. I would like to tear this code out of our product but the cost of doing that is probably not warranted.
What should I do?
Use only class methods to access and set items in your "array", don't use Array-specific syntax. And/or make the class dynamic.
EDIT I think you can leave everything as is and avoid making your class dynamic by defining only one additional method for item access (if it's not there for some reason). Something like:
public functon getItem(index:uint):*
{
if (index >= length) {
return null;
}
return this.slice(index, index+1)[0];
// this creates a redundant array on the fly, sorry.
}
// usage:
var people:People = ... init and fill with Person objects
for(var i:int=0; i<people.length(); i++){
doSomething(people.getItem(i));
}
And I know that is not the way it's meant to be answered on stackoverwlow, but... I can't hold it. ))
Anything extends Array -- is a heresy. If I see that in production code, I'll immediatelly proceed to initiating a "purge the unclean" sequence.
Just try googling for the subject a little bit, and you will see, that almost no one has got alive and well from an activitiy like that.
The main rational reason why this abomination is not meant to exist (aside form it being a heresy) is that you can not use array access [], you can not instantiate it like a normal array with items and you can not set items through array syntax [] and be notified of the changes somewhere in your class. But it is an Array by the right of birth, so any of your fellow developers not familiar with the subject may try to use it like a normal Array, because it's quite natural. And then they'll probably post another question on stackoverflow.
So, if you are still limited to just using class methods, what's the point in extending anyway? Whay not use neat aggregation/composition or proxy ways?
It's amazing, but there's even an article on extending Array on adobe.com. What they do not mention there is that you are likely to start burning in hell right away.
Array.length is a getter: it returns an int. people.length() is the same as calling 3(). I don't know how it worked when you tested that.
It looks like you'd be better off implementing something like IList and have addItem push into a Vector.<Person>. That will guarantee that you only have Person objects.
You should probably should not be extending Array. If you want to extend anything, extend Proxy (You can even use the ProxyArray class example with a Vector.<Person>). Extending top level classes (with the exception of Object) is often an invitation for confusion.
If you really, really want to extend Array, you have to make the class dynamic -- you are arbitrarily assigning and removing properties.
This looks like it works:
var s:String = "";
for each(var per:Person in people){
s += per.name + " ";
}
The People class has to be scripted as public dynamic class since it is extending the Array class.
From this Adobe help article: A subclass of Array should use the dynamic attribute, just as the Array class does. Otherwise, your subclass will not function properly.
You used the "push" function which apparently created an associative array.
Associative arrays can not be called by index. They can also not be reversed or have their order changed.
You need to access them by using the for..in loop
for (var key:String in people) {
trace("person : " + (people[key] as person).name);
}
// or as you found out the for each.. in
for each(var person:Person in people){
trace("person : " + person.name);
}
The arr.length() function of an associative array will always be 0 and you saw that with your error.
//Try this example you will see how an array can act like an array as we know it and an object.
var a:Array = [];
a[0] = true;
a.push(true);
a.push(true);
a["foo"] = true;
a.push(true);
a.push(true);
a.bar = true;
trace("for loop\n");
for(var i:int = 0, ilen:int = a.length ; i < ilen ; i++){
trace(i,a[int(i)]);
}
trace("\nfor...in loop\n");
for(var key:String in a){
trace(key,a[key]);
}
my question is simple, yet I couldn't find any answer of it in the net, maybe it's impossible to do...
The thing is that I have an ActionScript 3.0 application, and wanted to include a little one-line size textbox which showed all the trace() calls and such, which are shown in the console.
Has anyone got any idea of how can it be done? I would really appreciate it, as I have a full project with traces on it that I'd like to show, and it's now when I'm finishing that I'm realising I don't know how to do it :P
Of course not everything is lost, as I could just do my own class that showed there the messages, but it would be cleanier, and quicker not to have to replace all the trace() calls for my new class and method.
Regards and thanks in advance :)
I just did this last week.
There are logging frameworks for Flex out there. A shame, though, that Flex's logging only works in Debug mode. If you search SO for Flex logging you'll find various suggestions. None of them are amazing, IMO.
Finally I rolled my own by just creating a Log class with a static function that acts as a proxy for trace.
Something like:
public static myTrace(... args) : void { ... }
Then you just forward the args to trace but also to whatever other destination you want (e.g. an array of strings + dates) that you can then display in the log window.
Incidentally, I also used SwfAddress to trigger the log window whenever a certain parameter is added to the URL. Very handy.
Oh, what the heck.. here's the class. It just keeps the last 100 strings and there's also a "dump" function that you can invoke if you want to send the data to your server or just quickly print the entire history.
public class Log
{
public static var lines : ArrayList = new ArrayList();
public static const MAX_LINES : int = 100;
private static function logLine(line : String) : void
{
while (lines.length > MAX_LINES)
lines.removeItemAt(0);
lines.addItem({"line" : line, "time" : new Date()});
}
public static function logDump() : String
{
var ret : String = "";
for each (var entry : Object in lines.source)
{
ret = (entry.time as Date).toUTCString() + " " + entry.line + "\n" + ret;
}
return ret;
}
public static function debug(...args) : void
{
trace(args);
var line : String = "";
for (var i : int = 0; i < args.length; i++)
if (args[i] != null)
line += args[i].toString();
logLine(line);
}
}
Alternatively, you can use the ASDebugger
http://labs.flexperiments.nl/asdebugger-20-a-real-time-debugger-and-editor/
ASDebugger.debug( 'shallala' );
ASDebugger.debug_prop( variable );
Try to avoid using the debug display object option. The debugger can crash for complex objects (especially in flex)
You can probably do a simple replacement of 'trace(' to 'ASDebugger.debug('
i am using get and setters in my as3 code to edit values of an other class (because those variables are shared) i dont like to put stage.sharedVar.isScrabble in my code every time to change a variable so i used get/set functions
see below
private function get isScrabble(){return stage.sharedVar.isScrabble;}
private function set isScrabble(val){stage.sharedVar.isScrabble = val;}
private function get superGrid(){return stage.sharedVar.superGrid}
private function set superGrid(val){stage.sharedVar.superGrid = val;}
private function get gridSize(){return stage.sharedVar.gridSize}
private function set gridSize(val){stage.sharedVar.gridSize = val}
private function get blokDefaultWidth(){return stage.sharedVar.blokDefaultWidth}
private function set blokDefaultWidth(val){stage.sharedVar.blokDefaultWidth = val}
private function get blokDefaultHeight(){return stage.sharedVar.blokDefaultHeight}
private function set blokDefaultHeight(val){stage.sharedVar.blokDefaultHeight = val}
as you van see it has a lot of duplicate code every time the "return stage.sharedVar." and the "stage.sharedVar."+ the value + " = val" is constantly comming back.
i was wondering is there a other way of creating these get/sets?
something like?:
private function get variable1(){getValue("hisOwnFunctionName")}
private function set variable1(val){setValue("hisOwnFunctionName")}
and so on??
thanks,
Matthy
If I understand your question, you just want to get and set multiple properties on an Object. Depending on your situation you could try:
private function setProperty(name:Object,value:Object):void {
stage.sharedVar[name]=value;
}
private function getProperty(name:Object):Object {
return stage.sharedVar[name];
}
private function example():void {
setProperty("foo","bar");
trace(getProperty("foo")); //prints: bar
}
These functions will let you set and access the properties you want, and you wont have to keep changing the functions. It does mean that if you change superGrid to be something else, you can't just change the function, you have to change everywhere you use the get and setProperty. But it does mean that you don't have to keep writing new functions.
Hope this helps.
If you're in flex builder, download the web standard toolkit from http://download.eclipse.org/releases/galileo/ and use "snippets" to write these for you.
Otherwise, if you really need to do this within the program at run-time, you can make a wrapper proxy class to do all kinds of things: http://ltslashgt.com/2008/01/24/proxy-class-as3/