Attack of the CloneImportExport!

I know this is a relatively new feature introduced by our good buddy kevglass, but I seem to be having some trouble with it and can't seem to make it do my bidding.



I am able to load my model just fine apart from CloneImportExport, but when I modify my code to utilize it I get the following exception:

Exception in thread "Thread-7" java.lang.NullPointerException
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.write(CloneImportExport.java:566)
   at com.jme.scene.SceneElement.write(SceneElement.java:747)
   at com.jme.scene.batch.GeomBatch.write(GeomBatch.java:798)
   at com.jme.scene.batch.TriangleBatch.write(TriangleBatch.java:229)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
   at com.jme.scene.Geometry.write(Geometry.java:762)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.write(CloneImportExport.java:553)
   at com.jme.scene.SharedMesh.write(SharedMesh.java:561)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
   at com.jme.scene.Node.write(Node.java:657)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
   at com.jme.scene.Node.write(Node.java:657)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
   at com.jme.scene.Node.write(Node.java:657)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
   at com.jme.scene.Node.write(Node.java:657)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.access$2(CloneImportExport.java:194)
   at com.jme.util.CloneImportExport$CloneCapsule.writeSavableArrayList(CloneImportExport.java:631)
   at com.jme.scene.Node.write(Node.java:657)
   at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)
   at com.jme.util.CloneImportExport.saveClone(CloneImportExport.java:243)
   at com.captiveimagination.gb.spatial.Ship.initCollada(Ship.java:76)
   at com.captiveimagination.gb.spatial.Ship.<init>(Ship.java:24)
   at com.captiveimagination.gb.state.SectorOne.initScene(SectorOne.java:43)
   at com.captiveimagination.gb.state.SpaceGameState.init(SpaceGameState.java:67)
   at com.captiveimagination.gb.state.SpaceGameState.<init>(SpaceGameState.java:40)
   at com.captiveimagination.gb.state.SectorOne.<init>(SectorOne.java:25)
   at com.captiveimagination.gb.state.MenuGameState$2.run(MenuGameState.java:269)
   at java.lang.Thread.run(Thread.java:595)



The following code is what I execute in order to get this fun exception:

    private void initHull() throws IOException {
       if (cie == null) {
          URL url = getClass().getResource(SHIP_RESOURCE);
          Node model = (Node)BinaryImporter.getInstance().load(url.openStream());
          cie = new CloneImportExport();
          cie.saveClone(model);
       }
       attachChild((Spatial)cie.loadClone());
       setLocalScale(0.2f);
    }



Yes, I am invoking this in another thread, but I've tried interjecting directly into the OpenGL thread and it made no difference.  It is the "cie.saveClone(model);" line that is causing the exception to occur.  Any insights as to what I'm doing wrong or a bugfix would be greatly appreciated. ;)

Is it something with my model perhaps that the cloner is looking for an aspect that may not be set?



I was hoping my clever subject line would at least draw enough attention to get a response.  Or must I await a visit from kevglass?

Perhaps it has sth to do with this:

http://www.jmonkeyengine.com/jmeforum/index.php?topic=3844.15



There is a “Bug” in kevglass’ cloner, it uses a simple Hashmap, so if u clone a tree with nodes which want to save attribs with the same name, they will be overriden…

@see Thread.



Greetz

ARGH!



Kevglass, wherefore art though?

I just gave the code you posted a quick test, and got the same problem with a model of mine. After some quick debugger inspection, I think that it may be safe to just check for null in the method the first line of your stacktrace references:

at com.jme.util.CloneImportExport.save(CloneImportExport.java:197)


I believe that it should not provoke any problems to just check for null before calling that save method. Of course I could always be totally wrong, because I am not really aware of what I'm talking about here. Then again, I might be right just as well. It's "take it or leave it", I suppose...

This doesn't fix the problem it just pushes it off to another place where it blows up.  There are apparently several null meshes in my model…



Need some assistance, this is very far outside my range of expertise.

Well, from the little testing I had done it looked like the problem was an empty (null) entry somewhere in the Node's RenderStates, which I thought would be perfectly safe to ignore, as Nodes inherit RenderStates they don't have anyway. But as I said, I am possibly missing something important somewhere.

If I add a null check that simply returns false in the save(Savable) method in CloneImportExport at line 195 I get the following:


java.lang.NullPointerException
   at com.jme.util.CloneImportExport.create(CloneImportExport.java:315)
   at com.jme.util.CloneImportExport.access$6(CloneImportExport.java:313)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArray(CloneImportExport.java:1183)
   at com.jme.scene.SceneElement.read(SceneElement.java:765)
   at com.jme.scene.batch.GeomBatch.read(GeomBatch.java:812)
   at com.jme.scene.batch.TriangleBatch.read(TriangleBatch.java:236)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Geometry.read(Geometry.java:769)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavable(CloneImportExport.java:1157)
   at com.jme.scene.SharedMesh.read(SharedMesh.java:568)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport.load(CloneImportExport.java:221)
   at com.jme.util.CloneImportExport.loadClone(CloneImportExport.java:276)
   at com.captiveimagination.gb.spatial.Ship.initHull(Ship.java:79)
   at com.captiveimagination.gb.spatial.Ship.<init>(Ship.java:25)
   at com.captiveimagination.gb.state.SectorOne.initScene(SectorOne.java:43)
   at com.captiveimagination.gb.state.SpaceGameState.init(SpaceGameState.java:67)
   at com.captiveimagination.gb.state.SpaceGameState.<init>(SpaceGameState.java:40)
   at com.captiveimagination.gb.state.SectorOne.<init>(SectorOne.java:25)
   at com.captiveimagination.gb.state.MenuGameState$2.run(MenuGameState.java:269)
   at java.lang.Thread.run(Thread.java:595)
Exception in thread "Thread-7" java.lang.RuntimeException: java.lang.RuntimeException: java.lang.NullPointerException
   at com.jme.util.CloneImportExport.load(CloneImportExport.java:226)
   at com.jme.util.CloneImportExport.loadClone(CloneImportExport.java:276)
   at com.captiveimagination.gb.spatial.Ship.initHull(Ship.java:79)
   at com.captiveimagination.gb.spatial.Ship.<init>(Ship.java:25)
   at com.captiveimagination.gb.state.SectorOne.initScene(SectorOne.java:43)
   at com.captiveimagination.gb.state.SpaceGameState.init(SpaceGameState.java:67)
   at com.captiveimagination.gb.state.SpaceGameState.<init>(SpaceGameState.java:40)
   at com.captiveimagination.gb.state.SectorOne.<init>(SectorOne.java:25)
   at com.captiveimagination.gb.state.MenuGameState$2.run(MenuGameState.java:269)
   at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.RuntimeException: java.lang.NullPointerException
   at com.jme.util.CloneImportExport.create(CloneImportExport.java:322)
   at com.jme.util.CloneImportExport.access$6(CloneImportExport.java:313)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArray(CloneImportExport.java:1183)
   at com.jme.scene.SceneElement.read(SceneElement.java:765)
   at com.jme.scene.batch.GeomBatch.read(GeomBatch.java:812)
   at com.jme.scene.batch.TriangleBatch.read(TriangleBatch.java:236)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Geometry.read(Geometry.java:769)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavable(CloneImportExport.java:1157)
   at com.jme.scene.SharedMesh.read(SharedMesh.java:568)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport$CloneCapsule.readSavableArrayList(CloneImportExport.java:1245)
   at com.jme.scene.Node.read(Node.java:663)
   at com.jme.util.CloneImportExport.load(CloneImportExport.java:221)
   ... 9 more
Caused by: java.lang.NullPointerException
   at com.jme.util.CloneImportExport.create(CloneImportExport.java:315)
   ... 29 more



Thanks for coming back to help kev. ;)

One null after another…ouch…either my model has serious issues or there are deeper problems with this class in certain circumstances.



I can send you the model I'm using if you would be willing to investigate further?  Everytime I add a null check another null appears somewhere else and I'm stuck in a place where I don't understand what it's doing well enough to react to a null properly.



Not trying to seem ungrateful for this great class…I just want to be able to use it. :wink:

darkfrog said:

...
Everytime I add a null check another null appears somewhere else and I'm stuck in a place where I don't understand what it's doing well enough to react to a null properly.
...


try adding breakpoints instead of null ckecks :P
sfera said:

try adding breakpoints instead of null ckecks :P


Your sassy logic isn't welcome here sfera!  :x  :P
sfera said:

darkfrog said:

...
Everytime I add a null check another null appears somewhere else and I'm stuck in a place where I don't understand what it's doing well enough to react to a null properly.
...


try adding breakpoints instead of null ckecks :P


Too bad it's not logic. :P

I know what the problem is, the class simply doesn't handle it properly.  My model has null "things" inside it and the class wasn't designed to handle it.

Great, thanks a lot kevglass, I'll give it a shot this evening and see if it solves my problems.



You know, I almost wish that auto-boxing/un-boxing would convert null to/from 0 for numeric values.  Since you're obviously trying to get a primitive and nulls don't exist in primitives if you didn't already add your own checking for null it seems logical you'd just want it to assign 0 to it.  At the same time that seems wrong to have the system convert a null to a 0 for you.

darkfrog said:

You know, I almost wish that auto-boxing/un-boxing would convert null to/from 0 for numeric values


WARNING, this comment is pure opinion, and is not intentded to cause offence.

Eugh. No. for the love of <insert religious figure here>. No. Please shoot anyone who suggests that again :)

I hate that, if it's null, and I try to return it as something that can't be null, tell me, don't hide it from me and pretend it was 0. JDBC is the perfect example. If it's null, it probably means 'no value provided', if it's 0, it might mean, erm, 0 :)

Return me the null, it's my lookout (now I know about it) to decide what to do if it's null :).

Endolf

WARNING this comment is absolute truth and anyone who disagrees shall be burned until they change their mind!



hehe, I was about to say the same thing before I read your last post kevglass.  The only valid reason I could see against it is as a "check" for people who aren't really sure what they're writing and don't intend to actually autobox.  If that's the case, why not go ahead and check for null and then add your .floatValue() instead?  The purpose of autoboxing is convenience and if I know what I'm doing and don't care about nulls since I want a primitive anyway, then give me the convenience of casting and let it become 0 for me.



@Endolf, you borderline purist!  What are you doing using autoboxing in the first place?  Go back and hardcode your checks and conversions in the same place and you won't have any problem.  :stuck_out_tongue:

Hmmm, well, I tend to rarely use autoboxing, but when I find a good use of it, it comes in quite handy.



It took me a while to adopt the enhanced for loop, but there are a lot of performance enhancements as well as simplicity in code that it provides.

Once I get a chance to verify it fixes the problems I'll get it applied to CVS.



I'll drop a message on here once I've had a chance to apply it to my local copy.

darkfrog said:

@Endolf, you borderline purist!  What are you doing using autoboxing in the first place?  Go back and hardcode your checks and conversions in the same place and you won't have any problem.  :P


SmallTalk rules!!! ;)

Get rid of these nasty primative things, *everything* should be an object :)
Endolf said:

SmallTalk rules!!! ;)

Get rid of these nasty primative things, *everything* should be an object :)


HERETIC!  :-o

Sorry, I've had a lot of other things come up and haven't had the time yet.  I'll try to get on this ASAP and let you know.