Why does as3crypto say "Invalid padding value"? - actionscript-3

What is the cause of the error "Invalid padding value" in as3crypto?
Error: PKCS#5:unpad: Invalid padding value. expected [153], found [25]
at com.hurlant.crypto.symmetric::PKCS5/unpad()
at com.hurlant.crypto.symmetric::CTRMode/decrypt()
at com.hurlant.crypto.symmetric::SimpleIVMode/decrypt()
at com.mycompany.myproject::Application$/decrypt()
... (followed by the rest of my application stack)
I think I've previously solved this by making sure that the encrypted data is prepended with the intialization vector (IV), by using the SimpleIVMode wrapper class. In this case, I'm already doing that, though.
I'm not using the Crypto class because minimizing download size is important.
Any ideas?
My abstraction code (in Application class):
protected static var cipher:ICipher =
new SimpleIVMode(
new CTRMode(
new AESKey( Hex.toArray("53c12a8eb8612733ec817290580c3d") // not actual key
))
);
public static function encrypt(d:ByteArray):ByteArray {
d.position = 0;
cipher.encrypt(d);
d.position = 0;
return d;
}
public static function decrypt(d:ByteArray):ByteArray {
d.position = 0;
cipher.decrypt(d); // THIS LINE THROWS
d.position = 0;
return d;
}
All that d.position = 0 stuff is my paranoia.
The encryption code:
// we first have to serialize the object to a ByteArray, then encrypt that data.
var encryptedValue:ByteArray = new ByteArray();
encryptedValue.writeObject(objectToEncrypt);
encryptedValue.position = 0; // paranoia?
Application.encrypt(encryptedValue);
so.setProperty(key, encryptedValue); // save it in my SharedObject
Now the code that causes the error:
var data:ByteArray = so.data[key]; // get the byte array out of storage.
trace(data.length); // Check that it's real... I get 553 bytes
Application.decrypt(data); // THIS LINE THROWS

i think you should extend memory size of your SharedObject, like
so.flush(1000000000000000);

Related

compilation.Emit(..) create typeOf(T) with constructor params

I'm using RazorEngine for Razor Template parsing and trying to make use of its Roslyn code.
I know my issue is that I'm using a base-type (ViewBase<t>) in my razor views, and it's not being constructed (As it has a ctor parameter).
However after much googling, I can't find a way to tell Emit() how to make an instance of my type.
Below is the code it uses - the source code generates fine, and an omitted version is included below.
Is there some sort of factory of some sort I can provide in which it can use to generate this type?
When I call emit I get the error There is no argument given that corresponds to the required formal parameter 'componentContext' of 'ViewBase<MyModelType>.ViewBase(IComponentContext)' - So how would I tell emit() how to create a instance of my type ViewBase<T>
Creating an empty CTOR works fine - but isn't what I require.
public override Tuple<Type, CompilationData> CompileType(TypeContext context)
{
var sourceCode = GetCodeCompileUnit(context);
var assemblyName = GetAssemblyName(context);
(new PermissionSet(PermissionState.Unrestricted)).Assert();
var tempDir = GetTemporaryDirectory();
var sourceCodeFile = Path.Combine(tempDir, String.Format("{0}.{1}", assemblyName, SourceFileExtension));
File.WriteAllText(sourceCodeFile, sourceCode);
var references = GetAllReferences(context);
var compilation =
GetEmptyCompilation(assemblyName)
.AddSyntaxTrees(
GetSyntaxTree(sourceCode, sourceCodeFile))
.AddReferences(GetMetadataReferences(references));
compilation =
compilation
.WithOptions(
CreateOptions(context)
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary)
.WithPlatform(Platform.AnyCpu)
.WithSourceReferenceResolver(new RazorEngineSourceReferenceResolver(sourceCodeFile)));
var assemblyFile = Path.Combine(tempDir, String.Format("{0}.dll", assemblyName));
var assemblyPdbFile = Path.Combine(tempDir, String.Format("{0}.pdb", assemblyName));
var compilationData = new CompilationData(sourceCode, tempDir);
using (var assemblyStream = File.Open(assemblyFile, FileMode.Create, FileAccess.ReadWrite))
using (var pdbStream = File.Open(assemblyPdbFile, FileMode.Create, FileAccess.ReadWrite))
{
var opts = new EmitOptions()
.WithPdbFilePath(assemblyPdbFile);
var pdbStreamHelper = pdbStream;
if (IsMono())
{
opts = opts.WithDebugInformationFormat(DebugInformationFormat.PortablePdb);
}
var result = compilation.Emit(assemblyStream, pdbStreamHelper, options: opts);
}
}
My generated view code
namespace CompiledRazorTemplates.Dynamic
{
#line default
#line hidden
;
using System;
//my load of other using statements...
public class RazorEngine_99d043dd3e3d4c3ca787d42dd7a0bb6b : ViewBase<MyModelType>
{
#line hidden
public RazorEngine_99d043dd3e3d4c3ca787d42dd7a0bb6b()
{
}
#pragma warning disable 1998
public override async Task Execute()
{
..... OMITTED
}
}
}
public ViewBase(IComponentContext componentContext)
{
Contract.Requires(componentContext != null);
_componentContext = componentContext;
}
You're asking about the Razor compiler, not about Roslyn compilation.
I don't think there is any way to do that.
However, you could use a Roslyn CSharpSyntaxRewriter to find the existing constructor in your SyntaxTree and rewrite it to have & pass a parameter.

Saving objects with writeObject: What objects can be saved using this method?

I'm working making my first foray into the exciting world of byteArrays!
End Goal
I want to save the positions and other properties of each game element (in this case, blocks in a breakout clone) as part of a level design feature for my app, and also to more easily design levels for the game.
Current Approach
Convert data from a Vector of custom class instances (the bricks) into a ByteArray and save that data to a text file. It seems like this is working fine up to this point (can't be sure until I successfully extract that data back into a Vector object, because the text file that gets saved is pure gobbledygook).
I load a level design by reading the text file into a byteArray and then doing writeObject() into a Vector with the intention of now having a vector that contains all the bricks (this is not working).
The Problem
When I try to run my load function, the file loads, and the byteArray gets "filled" with data, but when I try to do writeObject, I get all these errors(one copy of the following errors for each brick in the vector).
TypeError: Error #1034: Type Coercion failed: cannot convert Object#92cdcb9 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object#92cde09 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object#92df041 to flash.geom.ColorTransform.
TypeError: Error #1034: Type Coercion failed: cannot convert Object#92df161 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object#92df281 to flash.geom.Point.
TypeError: Error #1034: Type Coercion failed: cannot convert Object#92df431 to flash.media.SoundTransform.
TypeError: Error #2004: One of the parameters is invalid.
My custom brick class is an extension of the Sprite class. But it additionally has properties that depend on Point and ColorTransform objects. Oddly, nowhere in my custom class do I have any reference to or use of SoundTransform... so that error seems glaringly odd. I'll post my custom class if anyone wants to look at it.
My Save and Load Methods
private function saveLevelDesign(brVec:Vector.<LineTestBlock>):void{
trace("save");
var file:File = File.documentsDirectory;
file = file.resolvePath("AnimationFiles/brickLevels/lev_001.txt");
fileStream.open(file,FileMode.WRITE);
var bytes:ByteArray = new ByteArray();
bytes = brickArrayToByteArray(brVec);
//fileStream.close();
}
private function loadLevelDesign():void{
trace("loadLevelDesign");
var file:File = File.documentsDirectory;
file = file.resolvePath("AnimationFiles/brickLevels/lev_001.txt");
fileStream.open(file,FileMode.READ);
file.addEventListener(IOErrorEvent.IO_ERROR,ioError);
file.addEventListener(Event.COMPLETE, loaded);
file.load();
//fileStream.open(file,FileMode.READ);
}
private function ioError(ioE:IOErrorEvent):void{
trace("oops",ioE);
}
private function loaded(e:Event):void{
trace("loaded");
var bytes:ByteArray = new ByteArray();
fileStream.readBytes(bytes);
trace(bytes.length,"bytes length"); // 0 bytes length
var vec:Vector.<LineTestBlock> = new Vector.<LineTestBlock>;
for (var i:int = 4; i < _playerTurn._brickArray.length; i++){
vec.push(_playerTurn._brickArray[i]);
}
bytes.writeObject(vec);
trace(bytes.length,"bytes length"); // 53516 bytes length
destroyBricks(_playerTurn); // just removes all bricks on the stage
vec = byteArrayToBrickArray(bytes); // calling this function throws all those errors
trace("vector length:",vec.length); // vector length 208 (this is the correct number of bricks, so that's good)
}
My Byte Conversion Methods
private function byteArrayToBrickArray(bytes:ByteArray):Vector.<LineTestBlock>{
bytes.position = 0;
var blocks:Vector.<LineTestBlock> = bytes.readObject() as Vector.<LineTestBlock>;
trace(bytes.position);
return blocks;
}
private function brickArrayToByteArray(brVec:Vector.<LineTestBlock>):ByteArray{
var bytes:ByteArray = new ByteArray();
/*for (var i:int = 0; i < brVec.length; i++){
if (brVec[i]._break == true){
bytes.writeObject(brVec[i]);
}
}*/
bytes.writeObject(brVec);
return bytes;
}
Anyone see if I doing something wrong, or not understanding something?
Any object that implements IExternalizable or is not a DisplayObject can be saved in a ByteArray and restored from one, if you write both readExternal and writeExternal methods correctly. If an object does not implement IExternalizable, Flash will attempt to write it using public components visible to the code, and read it by assigning values read to public properties in the same order. Normally you should use the interface with anything that's more complex than a Vector.<int>. Therefore, you need to implement IExternalizable in your LineTestBlock class, writing and reading only those properties that are required. Also, you can only use this method with objects that have an empty constructor, because in IDataInput.readObject the object is first constructed, then values are assigned.
The manual on IExternalizable. For some reason you can't access it from the normal class tree, but it is there and the interface is working.
I'd change your approach by encapsulating all the vectors, SoundTransforms etc into a single class, say Level, then implement IExternalizable in it, which will then write all the simple data types in order (remember to write vector's lengths before the data!) when asked to, then read itself from a byte array and reconstruct all the internal data structure in the meantime. An example:
import flash.utils.*;
public class Level implements flash.utils.IExternalizable
{
private var blocks:Vector.<LineTestBlock>;
// something extra
public function writeExternal(output:IDataOutput):void {
var l:int=blocks.length;
output.writeInt(l);
for (var i:int=0;i<l;i++) {
//write all the blocks[i]'s properties in order to output
}
// same approach to write all the extra properties
}
public function readExternal(input:IDataInput):void {
var l:int=input.readInt();
blocks=new Vector.<LineTestBlock>();
for (var i:int=0;i<l;i++) {
// first read all the properties into any local variables in the VERY SAME order they were written
// then create an instance of LineTestBlock
var block:LineTestBlock=new LineTestBlock(...);
// required parameters for the constructor should be read before creating object
// then fill remaining properties to the created instance
blocks.push(block); // and reconstruct the array
}
// same approach to every property that was saved
// reconstruct everything else that's depending on the data read
}
}
And finally, you would likely need to perform a flash.net.registerClassAlias() call somewhere in your app's initialization to have your Level be recognized as a serializable class.
bytes.readObject() return an Object.
so problem is about convertin Object to Vector.<LineTestBlock> so you have to convert it your self
private function byteArrayToBrickArray(bytes:ByteArray):Vector.<LineTestBlock>{
bytes.position = 0;
// Edit : readObject() only presents an Object
var blocks:Object = bytes.readObject();
trace(bytes.position);
/* you have to convert all step by step
at first we have to assume blocks as a vector
best way to searching its items is using _for key in_
*/
var converted:Vector.<LineTestBlock> = new Vector.<LineTestBlock>(blocks.length);
for (var key:String in blocks) {
converted.push(objectToLineTestBlock(blocks[key]));
}
return converted;
}
as i dont know structure of your LineTestBlock class, i cant provide "objectToLineTestBlock" function exactly
Here is an Example that simulates your LineTestBlock Class
my own LineTestBlock Class
public class LineTestBlock
{
public var w:int;
public var loc:Point;
public var stf:SoundTransform;
public function LineTestBlock(_w:int, _loc:Point, _stf:SoundTransform)
{
w = _w;
loc = _loc;
stf = _stf;
}
}
main class that testing the solution.
what i do is just converting all Objects to what really they are
bytearray.readObject() convert all classes to pure Objects
public class ByteTest extends Sprite
{
public function ByteTest()
{
var save_vector:Vector.<LineTestBlock> = new Vector.<LineTestBlock>();
var block_item1:LineTestBlock = new LineTestBlock(200, new Point(-1, 1), new SoundTransform(0.5));
var block_item2:LineTestBlock = new LineTestBlock(400, new Point(-2, 2), new SoundTransform(0.25));
save_vector.push(block_item1);
save_vector.push(block_item2);
var load_vector:Vector.<LineTestBlock>;
var bytes:ByteArray = new ByteArray();
bytes.writeObject(save_vector);
// trace(bytes.position);
load_vector = objectToLineTestVector(bytes);
// now test to check if everything is OK
trace(load_vector[1].stf.volume); // must print 0.25
}
public function objectToLineTestVector(bytes:ByteArray):Vector.<LineTestBlock> {
bytes.position = 0;
var loadedObject:Object = bytes.readObject();
var blocks:Vector.<LineTestBlock> = new Vector.<LineTestBlock>();
for (var key:String in loadedObject) {
blocks.push(objectToLineTestBlock(loadedObject[key])); // loadedObject[key] is a block_item1 and could be converted
}
return blocks;
}
public function objectToLineTestBlock(obj:Object):LineTestBlock {
return new LineTestBlock(obj.w, objectToPoint(obj.loc), objectToSoundTransform(obj.stf));
}
public function objectToPoint(obj:Object):Point {
return new Point(obj.x, obj.y);
}
public function objectToSoundTransform(obj:Object):SoundTransform {
return new SoundTransform(obj.volume);
}
}

View SharePoint 2010 list in JSON format

I am preparing to using Timeglider to create a timeline. One requirement is the data has to be in JSON format. One requirement for me is it needs to be client side as I do not have access to the servers or central admin.
When I try to do http://webname/_vti_bin/ListData.svc/listname I get an error for access permissions however when I issue it http://webname/subsite/_vti_bin/ListData.svc/listname I have no problem pulling data.
My situation is the list is on the TLD. I tried to follow this post How to retrieve a json object from a sharepoint list but it relates to SP 2007.
To implement pure JSON support in SharePoint 2007, 2010 and so on have a look at this project, http://camelotjson.codeplex.com/. It requires the commercial product Camelot .NET Connector to be installed on the server.
If you don't like to go commercial you can resort to the sp.js library, here is a small example I wrote, enjoy!
// Object to handle some list magic
var ListMagic = function () {
/* Private variables */
var that = this;
var clientContext = SP.ClientContext.get_current();
var web = clientContext.get_web();
var lists = web.get_lists();
/**
* Method to iterate all lists
*/
that.getLists = function () {
clientContext.load(lists);
clientContext.executeQueryAsync(execute, getFailed);
function execute() {
var listEnumerator = lists.getEnumerator();
while (listEnumerator.moveNext()) {
var l = listEnumerator.get_current();
// TODO! Replace console.log with actual routine
console.log(l.get_title());
}
}
function getFailed() {
// TODO! Implement fail management
console.log('Failed.');
}
};
/**
* Method to iterate all fields of a list
*/
that.getFields = function (listName) {
// Load list by listName, if not stated try to load the current list
var loadedList = typeof listName === 'undefined' ? lists.getById(SP.ListOperation.Selection.getSelectedList()) : that.lists.getByTitle(listName);
var fieldCollection = loadedList.get_fields();
clientContext.load(fieldCollection);
clientContext.executeQueryAsync(execute, getFailed);
function execute() {
var fields = fieldCollection.getEnumerator();
while (fields.moveNext()) {
var oField = fields.get_current();
// TODO! Replace console.log with actual routine
var listInfo = 'Field Title: ' + oField.get_title() + ', Field Name: ' + oField.get_internalName();
console.log(listInfo);
}
}
function getFailed() {
// TODO! Implement fail management
console.log('Failed.');
}
};
/**
* Method to get a specific listitem
*/
that.getListItem = function (itemId) {
var loadedList = lists.getById(SP.ListOperation.Selection.getSelectedList());
var spListItem = loadedList.getItemById(itemId);
clientContext.load(spListItem);
clientContext.executeQueryAsync(execute, getFailed);
function execute() {
// TODO! Replace console.log with actual routine
//spListItem.get_fieldValues()
console.log(spListItem.get_fieldValues()["Title"]);
}
function getFailed() {
// TODO! Implement fail management
console.log('Failed.');
}
};
/**
* Method to fake an init (optional)
*/
that.init = function () {
// Run any init functionality here
// I.e
that.getFields("Tasks");
};
return that;
};
// In case of no jquery use window.onload instead
$(document).ready(function () {
ExecuteOrDelayUntilScriptLoaded(function () {
var sp = new ListMagic();
sp.init();
}, 'sp.js');
});
Personally, I make HttpHandlers. I install them in the SharePoint isapi folder and the GAC and I can call them just like you might the owssvr.dll. http://servername/_vti_bin/myhttphandelr.dll
Pass it querystring variables or call it from jquery ajax. You can use the httpcontext and make a spcontext from it and have access to all sorts of information from the current location in SharePoint. Then you can javascriptserialize the objects and pass them as JSON. Looking for some code... Hang on... I can't put all the code but this should get you close. I use this to add a submenu to the context menu to allow a user to delete or rename a file if they uploaded it to a library and it is version 1.0 and to collect a file from a library and create a eml file with the selected file(s) as an attachment(s). We don't give our users delete privileges normally. Point being, you can now create a class with just the information you need from SharePoint and pass it as JSON. The only downfall I have with this, is iisreset is required if you make any changes to the dll.
I task schedule a iisreset every night at midnight anyway to keep it fresh and free from memory bloat. I come in the next day and my changes are there. The cool thing is, the spcontext has information about the current location in SharePoint from where it is called. So, http://servername/_vti_bin/myhttphandelr.dll vs http://servername/subsite/library/_vti_bin/myhttphandelr.dll
I might add. Don't try to serialize SharePoint objects. One they are huge, complex objects. Two, I don't think they are marked serializable. Just make you own class and populate it with the values you need from the SharePoint objects.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using System.Web;
using System.Web.Script.Serialization;
using ADODB;
using interop.cdosys;
using Microsoft.SharePoint;
namespace owssvr2
{
public class OWSsvr2 : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
private string cmd;
ctx ctx = new ctx();
private string currentuser;
private SPContext SPcontext;
private HttpContext cntx;
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
SPcontext = SPContext.GetContext(context); <-- Gets spcontext from the httpcontext
cntx = context;
ctx = GetData(context.Request); <-- I parse some information from the request to use in my app
cmd = ctx.Cmd;
ctx.User = context.User.Identity.Name;
currentuser = context.User.Identity.Name;
switch (cmd)
{
case "Delete":
Delete();
context.Response.Redirect(ctx.NextUsing);
break;
case "HasRights":
HasRights();
JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer();
string serEmployee = javaScriptSerializer.Serialize(ctx);
context.Response.Write(serEmployee);
context.Response.ContentType = "application/json; charset=utf-8";
break;
case "Rename":
Rename(context);
//context.Response.Redirect(context.Request["NextUsing"]);
break;
case "SendSingleFile":
try
{
context.Response.Clear();
context.Response.ClearHeaders();
context.Response.BufferOutput = true;
ADODB.Stream stream = SendSingleFile(context.Request["URL"]);
stream.Type = StreamTypeEnum.adTypeBinary;
stream.Position = 0;
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("content-disposition", "attachment;filename=Email.eml");
IStream iStream = (IStream)stream;
byte[] byteArray = new byte[stream.Size];
IntPtr ptrCharsRead = IntPtr.Zero;
iStream.Read(byteArray, stream.Size, ptrCharsRead);
context.Response.BinaryWrite(byteArray);
context.Response.End();
}
catch(Exception ex) {context.Response.Write(ex.Message.ToString()); }
break;
case "SendMultiFile":
try
{
//SendMultiFile(context.Request["IDs"]);
context.Response.Clear();
context.Response.ClearHeaders();
context.Response.BufferOutput = true;
ADODB.Stream stream = SendMultiFile(context.Request["IDs"]);
stream.Type = StreamTypeEnum.adTypeBinary;
stream.Position = 0;
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("content-disposition", "attachment;filename=Email.eml");
IStream iStream = (IStream)stream;
byte[] byteArray = new byte[stream.Size];
IntPtr ptrCharsRead = IntPtr.Zero;
iStream.Read(byteArray, stream.Size, ptrCharsRead);
context.Response.BinaryWrite(byteArray);
context.Response.End();
}
catch(Exception ex) {context.Response.Write("There was an error getting the files. </br>" + ex.Message.ToString()); }
break;
case "FileInfo":
JavaScriptSerializer javaScriptSerializer1 = new JavaScriptSerializer();
string serEmployee1 = javaScriptSerializer1.Serialize(FileInfo(context));
context.Response.Write(serEmployee1);
context.Response.ContentType = "application/json; charset=utf-8";
break;
case "UsersInGroups":
UsersInGroups ug = new UsersInGroups(context, context.Request["job"],context.Request["groups"]);
break;
}
}

converting element by using AS operator

So im creating something that now is finished and i want not to create every time elements, but to Pool them (ObjectPooling)
The problem comes that my object from the pool doesnt have the variable from the class it mimics, or at least i understand it that way, cause it doesnt do what it should.
Can someone tell me does this
var myNewBox:Box = Pool_myBox.getSprite() as Box;
mean that all the proparties and parameters that the class Box() has will be given and can be used by the new variable myNewBox or it`s a little more tricky that this?
or in other words is var myNewBox:Box = new Box();
the same as
var myNewBox:Box = Pool_myBox.getSprite() as Box;
------------EDIT-----------
so i do private var Pool_myBox:SpritePool; in the Main Class .
and set Pool_myBox = new SpritePool(Bullet,20); so in the 1st moment it has generated 20 objects.
The whole SpritePool class is this
package {
import flash.display.DisplayObject;
public class SpritePool {
private var pool:Array;
private var counter:int;
private var classRef:Class;
public function SpritePool(type:Class, len:int) {
pool = new Array();
counter = len;
classRef = type;
var i:int = len;
while (--i > -1) {
pool[i] = new type();
}
}
public function getSprite():DisplayObject {
if (counter > 0) {
return pool[--counter];
} else {
increasePool(10);
return getSprite();
}
}
public function increasePool(amount:int):void {
counter += amount;
while( --amount > -1 )
pool.unshift ( new classRef() );
}
public function returnSprite(s:DisplayObject):void {
pool[counter++] = s;
//trace(pool.length)
}
}
}
Absolutely not. If your getSprite() method does not return a Box instance (or some descendant of it), it will not 'inherit' the properties of Box. as is not performing any kind of internal magic - it is simply casting and telling the compiler that you know what you are doing and that the object indeed is a XXX instance (fill in). You should use casting only when going from a more general type to a more specific type, let's assume this:
var child:Sprite = parent.getChildAt(0); //what does this return? A display object instance => compiler will throw an error as Sprite is not DisplayObject
/*
Implicit coercion of a value with static type flash.display:DisplayObject to a possibly unrelated type flash.display:Sprite.
*/
//so you cast it:
var child:Sprite = parent.getChildAt(0) as Sprite; //this won't throw anything cos you casted it correctly
Also note that:
myObj as MyObj
is the same as:
MyObj(myObj)
if Pool_myBox.getSprite returns only Box objects, then you don't need to cast. The getSprite function should be look something like:
public function getSprite():Box {
var recycled_box:Box = ... // get box from pool
return recycled_box;
}
var myNewBox = Pool_myBox.getSprite();
Then, myNewBox will look and act like a Box. Note that any initialization or processing that happened on previous Box instances must be undone when it's returned to the pool if you need a "fresh" instance of Box.
OK, given the pool class, it looks like it should work with casting. Note that your text says you're passing in "Bullet" as the Class, while your code seems to want Box's (I assume this is either a typo, or Bullet is a superclass of Box?). If it works on the first 20, but not after you start recycling, then check what may need to be undone (as above).
What behavior are you seeing that makes you think it's not returning the right Class?

Shared objects not working correclty in flex -- Mobile

I have a very simple test going with shared objects in flex with mobile I have a person class.
package
{
import flash.display.MovieClip;
public class Person extends MovieClip
{
var personsname:String="";
public function Person(name:String)
{
personsname = name;
}
}
}
And then some simplish code in a view.
var person1:Person;
var person2:Person;
var person3:Person;
var person4:Person;
var thePeople:Array=[];
var so:SharedObject;
function init():void{
person1 = new Person("james");
person2 = new Person("mike");
person3 = new Person("Amanda");
person4 = new Person("Shelly");
thePeople.push(person1,person2,person3,person4);
//so = SharedObject.getLocal("savedData"); //clear it again
///so.clear(); // clear it again
savePeople();
getPeople();
}
private function savePeople():void{
so = SharedObject.getLocal("savedData");
if(so.data.thePeopleArray == null){
so.data.thePeopleArray = thePeople;
so.flush();
}
}
private function getPeople():void{
so = SharedObject.getLocal("savedData");
var thePeeps:Array = so.data.thePeopleArray;
trace(thePeeps);
}
The first time I run this it traces out
[object Person] 4 times
I close the emulator and rebuild and run it traces out
,,,
If I clear out the so it show the [object Person] again, but comment out get the ,,,
Can shared objects even store an array of objects properly. It is the same with the persistanceManager I believe.
The root of the problem here is that you are trying to save an instance MovieClip into the SharedObject. Since the MovieClip is an intrinsic object (native to flash) it cannot be converted into a form which can be stored. This causes flash to convert the data into a generic Object which is stored to disk. I can only guess at exactly what is going into the SharedObject at this point.
It seems to work the first time because flash does not actually load the shared object in the getPeople() call, it just uses the object which is already in memory. The second time the app runs it reads the generic object from disk and creates a generic object.
There is another problem which is that the flash player does not know to pass data to the constructor when it reads the object.
There are a few possible workarounds, some are:
Store the data as text
Store the data as a ByteArray
Store the data in a "Data Object"
Each of these requires some conversion during the read and write process, but this can be simplified using an interface. This also adds flexibility in case your object changes you will still be able to read the data in the SharedObject.
1: Text
As an example, you might add two methods to the Person object, call them serialise() and deserialise(). The serialise() method will return text which can be stored in the shared object. The deserialise() will parse text and populate the values of the object.
Here's a sample to illustrate this:
class Person {
private var name:String;
private var age:int;
public function serialise():String {
return [name, age].join("\t");
}
public function deserialise(input:String):void {
var tokens:Array = input.split("\t");
name = tokens[0];
age = parseInt(tokens[1]);
}
public static function create(name:String, age:int):Person
{
var output:Person = new Person();
output.name = name;
output.age = age;
return output;
}
}
For ease of use we can create a class for managing a collection of people:
class People {
private var people:Vector.<Person> = new Vector.<Person>();
public function clear():void {
people.splice(0, people.length);
}
public function add(person:Person):void {
people.push(person);
}
public function serialise():String {
var output:Array = [];
for each (var person:Person in people)
output.push(person.serialise());
return output.join("\n");
}
public function deserialise(input:String):void {
var tokens:Array = input.split("\n");
for each (var token:String in tokens) {
var person:Person = new Person();
person.deserialise(token);
add(person);
}
}
public function save():void {
var so:SharedObject = SharedObject.getLocal("cookie");
so.data.people = serialise();
so.flush();
}
public function load():void
{
var so:SharedObject = SharedObject.getLocal("cookie");
if (so.data.people != null)
deserialise(so.data.people);
}
}
Usage:
var people:People = new People();
people.load();
trace(people.serialise());
people.clear();
people.add(Person.create("Candy", 21));
people.add(Person.create("Sandy", 23));
people.add(Person.create("Randy", 27));
people.save();
trace(people.serialise());
An obvious flaw in this example is that the \n and \t characters cannot be used as part of the data (ie for the name of a person). This is a common short-coming with text data.
** Update: Look into the built-in JSON methods for a consistent approach to serialising objects to and from text.
2: ByteArray
Very similar to the text method described above, except the serialise/deserialise methods would accept an additional parameter of a ByteArray, which the object would write to. The ByteArray would then be saved and loaded from the shared object. The advantages of this method is that resulting data is usually is compact and versatile than the text method.
Flash also defines the IDataInput and IDataOutput interface which can be used here.
3: Data Objects
If you still prefer the storing objects directly, then you could create a proxy object which serves the sole purpose of carrying data. A data object (aka DO) is a an object which only has variables, and not methods. Eg:
class PersonDO {
public var name:String;
}
It would be used something like this:
var person2:Person;
var person3:Person;
var person4:Person;
var thePeople:Array=[];
var so:SharedObject;
function init():void{
person1 = new Person("james");
person2 = new Person("mike");
// store the people data into data objects
person1DO = new PersonDO();
person1DO.name = person1.name;
person2DO = new PersonDO();
person2DO.name = person2.name;
thePeople.push(person1DO,person2DO);
savePeople();
// load the people into data objects
getPeople();
person1 = new Person(thePeople[0].name);
person2 = new Person(thePeople[1].name);
private function savePeople():void{
so = SharedObject.getLocal("savedData");
if(so.data.thePeopleArray == null){
so.data.thePeopleArray = thePeople;
so.flush();
}
}
private function getPeople():void{
so = SharedObject.getLocal("savedData");
var thePeeps:Array = so.data.thePeopleArray;
trace(thePeeps);
}
Even though this may appear simpler than the alternatives there are downsides to storing objects directly:
- Stored data is very fragile - if you change the object then your data will become unusable unless you have several versions of each object.
- You need to ensure that a reference to the data object is compiled into the application.
- A common usage scenario for Shared Objects is to save data objects from one SWF, and load them in another. You need ensure that both SWFs use identical version of the class being saved and loaded.
Hope that helps.