ActionScript problem with prototype and static type variables - actionscript-3

I'm developing a flash (Flash 9, AS3) to connect to a server and send/receive/parse data to a chat on JavaScript/HTML.
I have a structure like this:
package {
public class myClass {
String.prototype.escapeHtml = function() {
var str = this.replace(/&/g, "&");
str = str.replace(/</g, "<");
str = str.replace(/>/g, ">");
return str;
}
function writeToBrowser(str:String) {
ExternalInterface.call("textWrite",str.escapeHtml());
}
}
}
When I compile it, I get this error:
1061: Call to a possibly undefined
method escapeHtml through a reference
with static type String.
If I remove the :String, it all works fine, but then I'd have to check if str is a String and if it's not undefined and so on.
I have many functions like this on my code, many of them receive user-entered data, so I think that removing the :String and doing many checks on every function isn't the best approach.
How can I make this right?

Then just define the function:
public function escapeHtml( str : String ) : String
{
var str = this.replace(/&/g, "&");
str = str.replace(/</g, "<");
str = str.replace(/>/g, ">");
return str;
}
in your class.
And call it:
public function writeToBrowser( str : String )
{
ExternalInterface.call( "textWrite", escapeHtml( str ) );
}
:)

you get an error because the compiler is in strict mode.
if you want to stay in strict mode you can try this:
ExternalInterface.call("textWrite",str["escapeHtml"]() );

Prototype is actually legacy.
You should extend the String class and use your custom class
package {
public class myClass {
public function writeToBrowser(str:CustomString) {
ExternalInterface.call("textWrite",str.escapeHtml());
}
}
public class CustomString {
public function escapeHtml():String {
var str = this.replace(/&/g, "&");
str = str.replace(/</g, "<");
str = str.replace(/>/g, ">");
return str;
}
}
}

Related

How can I solve 'Duplicate Constructor' error in Haxe?

In Haxe, I created a class named MyClass like:
class MyClass {
var score: String;
public function new (score: Int) {
this.score = Std.string(score);
}
public function new (score: String) {
this.score = score;
}
}
I need multiple constructors but Haxe does not allow me to do. It throws this error from building phase:
*.hx:*: lines * : Duplicate constructor
The terminal process terminated with exit code: 1
How can I solve this problem?
This is known as method overloading, which is not supported by Haxe apart from externs (but might be in the future). There's multiple ways you could work around this.
A common workaround in the case of constructors would be to have a static "factory method" for the second constructor:
class MyClass {
var score:String;
public function new(score:String) {
this.score = score;
}
public static function fromInt(score:Int):MyClass {
return new MyClass(Std.string(score));
}
}
You could also have a single constructor that accepts both kinds of arguments:
class MyClass {
var score:String;
public function new(score:haxe.extern.EitherType<String, Int>) {
// technically there's no need for an if-else in this particular case, since there's
// no harm in calling `Std.string()` on something that's already a string
if (Std.is(score, String)) {
this.score = score;
} else {
this.score = Std.string(score);
}
}
}
However, I wouldn't recommend this approach, haxe.extern.EitherType is essentially Dynamic under the hood, which is bad for type safety and performance. Also, EitherType is technically only intended to be used on externs.
A more type-safe, but also slightly more verbose option would be haxe.ds.Either<String, Int>. Here you'd have to explicitly call the enum constructors: new MyClass(Left("100")) / new MyClass(Right(100)), and then use pattern matching to extract the value.
An abstract type that supports implicit conversions from String and Int might also be an option:
class Test {
static function main() {
var s1:Score = "100";
var s2:Score = 100;
}
}
abstract Score(String) from String {
#:from static function fromInt(i:Int):Score {
return Std.string(i);
}
}
Finally, there's also an experimental library that adds overloading support with macros, but I'm not sure if it supports constructors.
I recommend to use type parameter
class MyClass<T> {
var score:String;
public function new(score:T) {
this.score = Std.string(score);
}
}
You can also use type parameter at constructor
class MyClass {
var score:String;
public function new<T>(score:T) {
this.score = Std.string(score);
}
}
However, T used at constructor fails at runtime (CS and Java), it is not fixed yet (Haxe 4). Otherwise, you could do this
class MyClass {
var score:String;
#:generic public function new<#:const T>(score:T) {
this.score = Std.is(T, String) ? untyped score : Std.string(score);
}
}
which nicely produce code like this (CS)
__hx_this.score = ( (( T is string )) ? (score) : (global::Std.#string(score)) );
causing Std.string() to be called only if T is not a String.
Hej,
With a simple example as it is, you can just do something like that function new( ?s : String, ?n : Int ){} and Haxe will use the correct argument by type. But you'll be able to do new() and maybe you don't want.

Google end point returns JSON for long data type in quotes

I am using Google cloud end point for my rest service. I am consuming this data in a GWT web client using RestyGWT.
I noticed that cloud end point is automatically enclosing a long datatype in double quotes which is causing an exception in RestyGWT when I try to convert JSON to POJO.
Here is my sample code.
#Api(name = "test")
public class EndpointAPI {
#ApiMethod(httpMethod = HttpMethod.GET, path = "test")
public Container test() {
Container container = new Container();
container.testLong = (long)3234345;
container.testDate = new Date();
container.testString = "sathya";
container.testDouble = 123.98;
container.testInt = 123;
return container;
}
public class Container {
public long testLong;
public Date testDate;
public String testString;
public double testDouble;
public int testInt;
}
}
This is what is returned as JSON by cloud end point. You can see that testLong is serialized as "3234345" rather than 3234345.
I have the following questions.
(1) How can I remove double quotes in long values ?
(2) How can I change the string format to "yyyy-MMM-dd hh:mm:ss" ?
Regards,
Sathya
What version of restyGWT are you using ? Did you try 1.4 snapshot ?
I think this is the code (1.4) responsible for parsing a long in restygwt, it might help you :
public static final AbstractJsonEncoderDecoder<Long> LONG = new AbstractJsonEncoderDecoder<Long>() {
public Long decode(JSONValue value) throws DecodingException {
if (value == null || value.isNull() != null) {
return null;
}
return (long) toDouble(value);
}
public JSONValue encode(Long value) throws EncodingException {
return (value == null) ? getNullType() : new JSONNumber(value);
}
};
static public double toDouble(JSONValue value) {
JSONNumber number = value.isNumber();
if (number == null) {
JSONString val = value.isString();
if (val != null){
try {
return Double.parseDouble(val.stringValue());
}
catch(NumberFormatException e){
// just through exception below
}
}
throw new DecodingException("Expected a json number, but was given: " + value);
}
return number.doubleValue();
}

Deserializing a JSON string to a class instance in Haxe

I am trying to deserialize a JSON string into a class instance in Haxe.
class Action
{
public var id:Int;
public var name:String;
public function new(id:Int, name:String)
{
this.id = id;
this.name = name;
}
}
I would like to do something like this:
var action:Action = haxe.Json.parse(actionJson);
trace(action.name);
However, this produces an error:
TypeError: Error #1034: Type Coercion failed: cannot convert Object#3431809 to Action
Json doesn't have a mechanism to map language specific data types and only supports a subset of the data types included in JS. To keep the information about the Haxe types you can certainly build your own mechanism.
// This works only for basic class instances but you can extend it to work with
// any type.
// It doesn't work with nested class instances; you can detect the required
// types with macros (will fail for interfaces or extended classes) or keep
// track of the types in the serialized object.
// Also you will have problems with objects that have circular references.
class JsonType {
public static function encode(o : Dynamic) {
// to solve some of the issues above you should iterate on all the fields,
// check for a non-compatible Json type and build a structure like the
// following before serializing
return haxe.Json.stringify({
type : Type.getClassName(Type.getClass(o)),
data : o
});
}
public static function decode<T>(s : String) : T {
var o = haxe.Json.parse(s),
inst = Type.createEmptyInstance(Type.resolveClass(o.type));
populate(inst, o.data);
return inst;
}
static function populate(inst, data) {
for(field in Reflect.fields(data)) {
Reflect.setField(inst, field, Reflect.field(data, field));
}
}
}
I extended Franco's answer to allow for recursively containing objects within your json objects, as long as the _explicitType property is set on that object.
For instance, the following json:
{
intPropertyExample : 5,
stringPropertyExample : 'my string',
pointPropertyExample : {
_explicitType : 'flash.geom.Point',
x : 5,
y : 6
}
}
will correctly be serialized into an object whose class looks like this:
import flash.geom.Point;
class MyTestClass
{
public var intPropertyExample:Int;
public var stringPropertyExample:String;
public var pointPropertyExample:Point;
}
when calling:
var serializedObject:MyTestClass = EXTJsonSerialization.decode([string of json above], MyTestClass)
Here's the code (note that it uses TJSON as a parser, as CrazySam recommended):
import tjson.TJSON;
class EXTJsonSerialization
{
public static function encode(o : Dynamic)
{
return TJSON.encode(o);
}
public static function decode<T>(s : String, typeClass : Class<Dynamic>) : T
{
var o = TJSON.parse(s);
var inst = Type.createEmptyInstance(typeClass);
EXTJsonSerialization.populate(inst, o);
return inst;
}
private static function populate(inst, data)
{
for (field in Reflect.fields(data))
{
if (field == "_explicitType")
continue;
var value = Reflect.field(data, field);
var valueType = Type.getClass(value);
var valueTypeString:String = Type.getClassName(valueType);
var isValueObject:Bool = Reflect.isObject(value) && valueTypeString != "String";
var valueExplicitType:String = null;
if (isValueObject)
{
valueExplicitType = Reflect.field(value, "_explicitType");
if (valueExplicitType == null && valueTypeString == "Array")
valueExplicitType = "Array";
}
if (valueExplicitType != null)
{
var fieldInst = Type.createEmptyInstance(Type.resolveClass(valueExplicitType));
populate(fieldInst, value);
Reflect.setField(inst, field, fieldInst);
}
else
{
Reflect.setField(inst, field, value);
}
}
}
}
A modern, macro-based library for this purpose is json2object. It can be used like this:
var parser = new json2object.JsonParser<Action>();
var action:Action = parser.fromJson('{"id": 0, "name": "run"}', "action.json");
Another option, also macro-powered, is tink_json. In this case it's a bit more verbose because it requires you to specifiy how exactly a class should be parsed using #:jsonParse metadata:
#:jsonParse(function(json) return new Action(json.id, json.name))
class Action {
// ...
Parsing is a one-liner:
var action:Action = tink.Json.parse('{"id": 0, "name": "run"}');

ActionScript - Determine If Value is Class Constant

i'd like to throw an argument error if a particular function doesn't work without a passed value that also happens to be a public constant of the class containing the function.
is there anyway to determine if a class owns a public constant instead of having to iterate thru all of them?
something like this:
public static const HALIFAX:String = "halifax";
public static const MONTREAL:String = "montreal";
public static const TORONTO:String = "toronto";
private var cityProperty:String;
public function set city(value:String):void
{
if (!this.hasConstant(value))
throw new ArgumentError("set city value is not applicable.");
cityProperty = value;
}
public function get city():Strig
{
return cityProperty;
}
currently, for this functionality i have to write the city setter function like this:
public function set city(value:String):void
{
if (value != HALIFAX && value != MONTREAL && value != TORONTO)
throw new ArgumentError("set city value is not applicable.");
cityProperty = value;
}
is this the only way to accomplish this task?
Yes, if you use reflections:
private var type:Class;
private var description:XML;
private function hasConstant (str : String ) : Boolean
{
if (description == null)
{
type = getDefinitionByName (getQualifiedClassName (this)) as Class;
description = describeType (type);
}
for each ( var constant:XML in description.constant)
{
if (type[constant.#name] == str) return true;
}
return false;
}
Note that for this to work, all constants must always be String objects declared public static const.
I was looking for an answer to this question myself and found it annoying that hasOwnProperty() did not work for static properties. Turns out though, that if you cast your class to a Class object, it does work.
Here's an example:
public final class DisplayMode
{
public static const one: String = "one";
public static const two: String = "two";
public static const three: String = "three";
public static function isValid(aDisplayMode: String): Boolean {
return Class(DisplayMode).hasOwnProperty(aDisplayMode);
}
}
I owe this solution to jimmy5804 from this discussion, so hats off to him.
You should be able to use bracket notation to do this. For example:
var foo:Sprite = new Sprite();
foo.rotation = 20;
trace( foo["x"], foo["rotation"]); // traces "0 20"
or more specific to your case:
var bar:String = "rotation";
trace( foo[bar] ); // traces "20"
The only thing you have to look out for here, is that the bracket accessor will throw a ReferenceError if you ask for an object property that isn't there, such as:
trace ( foo["cat"] ); // throws ReferenceError
But it will not throw if you are asking for a static property:
trace ( Sprite["cat"] ); // traces "undefined"
So in your case you might try:
if ( this[value] == undefined ) {
throw new ArgumentError("set city value is not applicable.");
}
EDIT:
Sorry, I was confusing the const's names with their values.
For this to work on your problem you would have to make the String value the same as the const's name, so for example:
public static const HALIFAX:String = "HALIFAX";
then you could use the query as described above and it would give you the desired result.

Get all static variables in a class

I have this ObjectType class which is a class to help me do something like this:
object.type = ObjectType.TWO
//ObjectType.as
package
{
public class ObjectType
{
public static var ONE:String = "one";
public static var TWO:String = "two";
public static var THREE:String = "three";
public function ObjectType()
{
}
}
}
Let's suppose I'm creating a new class and I need a property named type. In that property set function I want to make sure that it's value is one of the ObjectType variables. How can I achieve this?
public function set type(value:String):void
{
for (var o:Object in ObjectType) {
if (value == o)
this._type = value;
} else {
//error
}
}
}
Not performance aware but without modifying anything you can use describeType function to check the static field and get the value back:
function valueInClass(clazz:Class, value:*):Boolean {
return describeType(clazz).variable.(clazz[#name.toString()] == value).length() != 0
}
public function set type(value:String):void
{
if (valueInClass(ObjectType, value)) {
this._type = value;
} else {
//error
}
}
I suppose the second code example you presented doesn't work...
I think it is because you're using the for in loop a little bit wrong.
for (var blah:String in somewhere){
// blah represents a KEY of the somewhere object
// to get the value of this key, use:
var theValue = somewhere[blah];
}
It's the for each loop that loops through the values. But for now I'll use the for in.
Also, it's not in ObjectType, but rather in the class' prototype, that is in ObjectType.prototype.
So, to fix this:
for (var o:* in ObjectType.prototype) {
if (value == ObjectType.prototype[o])
this._type = value;
} else {
//error
}
}
You can solve this using reflection.
A similar question was asked just a few days ago, you should be able to use the same solution, found here.
It should be noted that while the the accepted answer is right, it's also really slow. Not something that you want to do a lot. There are three simpler solutions.
One: Check the value itself:
public function set type(value:String):void
{
if( value != ObjectType.ONE && value != ObjectType.TWO && value != ObjectType.THREE )
return;
}
Obviously, the more constants you have the check the harder this becomes.
Two: Use ints as your constants
Change your ObjectType class to use ints:
public class ObjectType
{
public static var NONE:int = 0;
public static var ONE:int = 1;
public static var TWO:int = 2;
public static var THREE:int = 3;
public static var TOTAL:int = 4;
}
Notice the NONE and TOTAL in there? This makes it easy to check if your value is in the right range:
public function set type(value:int):void
{
if( value <= ObjectType.NONE || value >= ObjectType.TOTAL )
return;
}
You can add more values as needed and you just need to update TOTAL and it'll still work. This needs each value to be in order though.
Three: Use Enums
While Flash has no in-build class for enums, there's a lot of solutions available. Check our the Enum class from Scott Bilas: http://scottbilas.com/blog/ultimate-as3-fake-enums/
Using this as your base class your ObjectType class becomes:
public final class ObjectType extends Enum
{
{ initEnum( ObjectType ); } // static ctor
public static const ONE:ObjectType = new ObjectType;
public static const TWO:ObjectType = new ObjectType;
public static const THREE:ObjectType = new ObjectType;
}
And your check now becomes:
public function set type(value:ObjectType):void
{
...
}
Here, your setter now becomes type safe and will throw errors if anything other than an ObjectType is used.
It turns out that if using an ENUM type of check you should check for the constants property, not variables as showin in the example here:
ActionScript - Determine If Value is Class Constant