diff --git a/docs/todos/work/2025-01-11-03-02-54-test-suite/task.md b/docs/todos/work/2025-01-11-03-02-54-test-suite/task.md index f04174929..50569709f 100644 --- a/docs/todos/work/2025-01-11-03-02-54-test-suite/task.md +++ b/docs/todos/work/2025-01-11-03-02-54-test-suite/task.md @@ -3,7 +3,7 @@ **Status:** In Progress **Created:** 2025-01-11T03:02:54 **Started:** 2025-01-11T03:11:22 -**Agent PID:** 93834 +**Agent PID:** 15570 **CRITICAL:** - NEVER never check a chceckbox and move on to the next checkbox unless the user has confirmed completion of the current checkbox! @@ -237,18 +237,94 @@ The test programs will print both SkeletonData (setup pose/static data) and Skel - Create mechanism in tests/generate-cpp-serializer.ts to replace auto-generated functions with hand-written ones - Implement custom writeSkin function that properly handles AttachmentMap::Entries iteration - [x] Create regenerate-all.sh script in tests/ that runs all generators in sequence - - [ ] Fix C++ JsonWriter output formatting issues (array formatting broken) - - [ ] Test with sample skeleton files -- [ ] TypeScript (spine-ts): - - [ ] Follow what we did for spine-cpp wrt to JsonWriter, SkeletonSerializer and the generator -- [ ] C# - - [ ] Figure out how we can add the HeadlessTest and run it without adding it to the assembly itself - - [ ] Follow what we did for spine-cpp wrt -- [ ] C (spine-c): - - [ ] Follow what we did for spine-cpp wrt to JsonWriter, SkeletonSerializer and the generator (this one will need some ultrathink and discussion with the user before code changs) -- [ ] Update tests/README.md to describe the new setup + - [x] ~~Fix C++ JsonWriter output formatting issues (array formatting broken)~~ + - [x] ~~Test with sample skeleton files~~ +- [x] ~~TypeScript (spine-ts):~~ + - [x] ~~Follow what we did for spine-cpp wrt to JsonWriter, SkeletonSerializer and the generator~~ +- [x] ~~C#~~ + - [x] ~~Figure out how we can add the HeadlessTest and run it without adding it to the assembly itself~~ + - [x] ~~Follow what we did for spine-cpp wrt~~ +- [x] ~~C (spine-c):~~ + - [x] ~~Follow what we did for spine-cpp wrt to JsonWriter, SkeletonSerializer and the generator (this one will need some ultrathink and discussion with the user before code changs)~~ +- [x] ~~Update tests/README.md to describe the new setup~~ +## Phase 3: Intermediate Representation for Cross-Language Serializer Generation + +### Problem +Current approach requires maintaining separate generator logic for each language (Java, C++, C, TypeScript, C#). The complex analysis, exclusion handling, and serialization logic is duplicated across generators, making maintenance difficult. + +### Solution: Language-Agnostic Intermediate Representation (IR) +Create a single IR generator that captures all serialization logic in a JSON format that language-specific generators can consume without a lot of post-processing. The IR will still +contain Java specific types and names. Language specific generators then just have to translate. + +### IR Structure +```typescript +interface SerializerIR { + publicMethods: PublicMethod[]; + writeMethods: WriteMethod[]; + enumMappings: { [enumName: string]: { [javaValue: string]: string } }; +} + +type Property = Primitive | Object | Enum | Array | NestedArray; + +interface Primitive { + kind: "primitive"; + name: string; // "duration" + getter: string; // "getDuration" + valueType: string; // "float", "int", "boolean", "String" + isNullable: boolean; +} + +interface Object { + kind: "object"; + name: string; // "color" + getter: string; // "getColor" + valueType: string; // "Color" + writeMethodCall: string; // "writeColor" + isNullable: boolean; +} + +interface Enum { + kind: "enum"; + name: string; // "mixBlend" + getter: string; // "getMixBlend" + enumName: string; // "MixBlend" + isNullable: boolean; +} + +interface Array { + kind: "array"; + name: string; // "timelines" + getter: string; // "getTimelines" + elementType: string; // "Timeline", "int", "String" + elementKind: "primitive" | "object"; + writeMethodCall?: string; // "writeTimeline" (for objects) + isNullable: boolean; +} + +interface NestedArray { + kind: "nestedArray"; + name: string; // "vertices" + getter: string; // "getVertices" + elementType: string; // "float" + isNullable: boolean; +} +``` + +### Implementation Plan +- [x] Create tests/generate-serializer-ir.ts: + - [x] Reuse logic from tests/generate-java-serializer.ts (analysis, exclusions, property detection) + - [x] Output SerializerIR as JSON to tests/output/serializer-ir.json + - [x] All exclusions and filtering pre-applied - no post-processing needed +- [ ] Update language generators to consume IR: + - [x] Replace tests/generate-java-serializer.ts with IR-based version + - [ ] Modify tests/generate-cpp-serializer.ts to use IR + - [ ] Create tests/generate-ts-serializer.ts using IR + - [ ] Create tests/generate-cs-serializer.ts using IR + - [ ] Language generators focus purely on syntax transformation +- [ ] Update tests/regenerate-all.sh to generate IR first, then all languages + ### Misc (added by user while Claude worked, need to be refined!) - [ ] HeadlessTest should probably - Have a mode that does what we currently do: take files and animation name, and output serialized skeleton data and skeleton. Used for ad-hoc testing of files submitted by users in error reports etc. diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/utils/SkeletonSerializer.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/utils/SkeletonSerializer.java index b35b07ff3..4e669d781 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/utils/SkeletonSerializer.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/utils/SkeletonSerializer.java @@ -20,32 +20,33 @@ import java.util.HashSet; public class SkeletonSerializer { private final Set visitedObjects = new HashSet<>(); + private JsonWriter json; public String serializeSkeletonData(SkeletonData data) { visitedObjects.clear(); - JsonWriter json = new JsonWriter(); - writeSkeletonData(json, data); + json = new JsonWriter(); + writeSkeletonData(data); json.close(); return json.getString(); } public String serializeSkeleton(Skeleton skeleton) { visitedObjects.clear(); - JsonWriter json = new JsonWriter(); - writeSkeleton(json, skeleton); + json = new JsonWriter(); + writeSkeleton(skeleton); json.close(); return json.getString(); } public String serializeAnimationState(AnimationState state) { visitedObjects.clear(); - JsonWriter json = new JsonWriter(); - writeAnimationState(json, state); + json = new JsonWriter(); + writeAnimationState(state); json.close(); return json.getString(); } - private void writeAnimation(JsonWriter json, Animation obj) { + private void writeAnimation(Animation obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -59,7 +60,7 @@ public class SkeletonSerializer { json.writeName("timelines"); json.writeArrayStart(); for (Timeline item : obj.getTimelines()) { - writeTimeline(json, item); + writeTimeline(item); } json.writeArrayEnd(); @@ -67,11 +68,7 @@ public class SkeletonSerializer { json.writeValue(obj.getDuration()); json.writeName("bones"); - json.writeArrayStart(); - for (int i = 0; i < obj.getBones().size; i++) { - json.writeValue(obj.getBones().get(i)); - } - json.writeArrayEnd(); + writeIntArray(obj.getBones()); json.writeName("name"); json.writeValue(obj.getName()); @@ -79,7 +76,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeAlphaTimeline(JsonWriter json, Animation.AlphaTimeline obj) { + private void writeAlphaTimeline(Animation.AlphaTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -119,7 +116,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeAttachmentTimeline(JsonWriter json, Animation.AttachmentTimeline obj) { + private void writeAttachmentTimeline(Animation.AttachmentTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -166,7 +163,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeDeformTimeline(JsonWriter json, Animation.DeformTimeline obj) { + private void writeDeformTimeline(Animation.DeformTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -181,20 +178,16 @@ public class SkeletonSerializer { json.writeValue(obj.getFrameCount()); json.writeName("attachment"); - writeVertexAttachment(json, obj.getAttachment()); + writeVertexAttachment(obj.getAttachment()); json.writeName("vertices"); json.writeArrayStart(); for (float[] nestedArray : obj.getVertices()) { - if (nestedArray == null) { - json.writeNull(); - } else { - json.writeArrayStart(); - for (float elem : nestedArray) { - json.writeValue(elem); - } - json.writeArrayEnd(); + json.writeArrayStart(); + for (float elem : nestedArray) { + json.writeValue(elem); } + json.writeArrayEnd(); } json.writeArrayEnd(); @@ -224,7 +217,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeDrawOrderTimeline(JsonWriter json, Animation.DrawOrderTimeline obj) { + private void writeDrawOrderTimeline(Animation.DrawOrderTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -241,15 +234,11 @@ public class SkeletonSerializer { json.writeName("drawOrders"); json.writeArrayStart(); for (int[] nestedArray : obj.getDrawOrders()) { - if (nestedArray == null) { - json.writeNull(); - } else { - json.writeArrayStart(); - for (int elem : nestedArray) { - json.writeValue(elem); - } - json.writeArrayEnd(); + json.writeArrayStart(); + for (int elem : nestedArray) { + json.writeValue(elem); } + json.writeArrayEnd(); } json.writeArrayEnd(); @@ -276,7 +265,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeEventTimeline(JsonWriter json, Animation.EventTimeline obj) { + private void writeEventTimeline(Animation.EventTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -293,7 +282,7 @@ public class SkeletonSerializer { json.writeName("events"); json.writeArrayStart(); for (Event item : obj.getEvents()) { - writeEvent(json, item); + writeEvent(item); } json.writeArrayEnd(); @@ -320,7 +309,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeIkConstraintTimeline(JsonWriter json, Animation.IkConstraintTimeline obj) { + private void writeIkConstraintTimeline(Animation.IkConstraintTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -360,7 +349,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeInheritTimeline(JsonWriter json, Animation.InheritTimeline obj) { + private void writeInheritTimeline(Animation.InheritTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -400,7 +389,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePathConstraintMixTimeline(JsonWriter json, Animation.PathConstraintMixTimeline obj) { + private void writePathConstraintMixTimeline(Animation.PathConstraintMixTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -440,7 +429,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePathConstraintPositionTimeline(JsonWriter json, Animation.PathConstraintPositionTimeline obj) { + private void writePathConstraintPositionTimeline(Animation.PathConstraintPositionTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -480,7 +469,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePathConstraintSpacingTimeline(JsonWriter json, Animation.PathConstraintSpacingTimeline obj) { + private void writePathConstraintSpacingTimeline(Animation.PathConstraintSpacingTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -520,7 +509,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintDampingTimeline(JsonWriter json, Animation.PhysicsConstraintDampingTimeline obj) { + private void writePhysicsConstraintDampingTimeline(Animation.PhysicsConstraintDampingTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -560,7 +549,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintGravityTimeline(JsonWriter json, Animation.PhysicsConstraintGravityTimeline obj) { + private void writePhysicsConstraintGravityTimeline(Animation.PhysicsConstraintGravityTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -600,7 +589,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintInertiaTimeline(JsonWriter json, Animation.PhysicsConstraintInertiaTimeline obj) { + private void writePhysicsConstraintInertiaTimeline(Animation.PhysicsConstraintInertiaTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -640,7 +629,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintMassTimeline(JsonWriter json, Animation.PhysicsConstraintMassTimeline obj) { + private void writePhysicsConstraintMassTimeline(Animation.PhysicsConstraintMassTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -680,7 +669,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintMixTimeline(JsonWriter json, Animation.PhysicsConstraintMixTimeline obj) { + private void writePhysicsConstraintMixTimeline(Animation.PhysicsConstraintMixTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -720,7 +709,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintResetTimeline(JsonWriter json, Animation.PhysicsConstraintResetTimeline obj) { + private void writePhysicsConstraintResetTimeline(Animation.PhysicsConstraintResetTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -760,7 +749,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintStrengthTimeline(JsonWriter json, Animation.PhysicsConstraintStrengthTimeline obj) { + private void writePhysicsConstraintStrengthTimeline(Animation.PhysicsConstraintStrengthTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -800,7 +789,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintWindTimeline(JsonWriter json, Animation.PhysicsConstraintWindTimeline obj) { + private void writePhysicsConstraintWindTimeline(Animation.PhysicsConstraintWindTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -840,7 +829,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeRGB2Timeline(JsonWriter json, Animation.RGB2Timeline obj) { + private void writeRGB2Timeline(Animation.RGB2Timeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -880,7 +869,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeRGBA2Timeline(JsonWriter json, Animation.RGBA2Timeline obj) { + private void writeRGBA2Timeline(Animation.RGBA2Timeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -920,7 +909,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeRGBATimeline(JsonWriter json, Animation.RGBATimeline obj) { + private void writeRGBATimeline(Animation.RGBATimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -960,7 +949,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeRGBTimeline(JsonWriter json, Animation.RGBTimeline obj) { + private void writeRGBTimeline(Animation.RGBTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1000,7 +989,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeRotateTimeline(JsonWriter json, Animation.RotateTimeline obj) { + private void writeRotateTimeline(Animation.RotateTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1040,7 +1029,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeScaleTimeline(JsonWriter json, Animation.ScaleTimeline obj) { + private void writeScaleTimeline(Animation.ScaleTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1080,7 +1069,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeScaleXTimeline(JsonWriter json, Animation.ScaleXTimeline obj) { + private void writeScaleXTimeline(Animation.ScaleXTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1120,7 +1109,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeScaleYTimeline(JsonWriter json, Animation.ScaleYTimeline obj) { + private void writeScaleYTimeline(Animation.ScaleYTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1160,7 +1149,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSequenceTimeline(JsonWriter json, Animation.SequenceTimeline obj) { + private void writeSequenceTimeline(Animation.SequenceTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1178,7 +1167,7 @@ public class SkeletonSerializer { json.writeValue(obj.getSlotIndex()); json.writeName("attachment"); - writeAttachment(json, obj.getAttachment()); + writeAttachment(obj.getAttachment()); json.writeName("propertyIds"); json.writeArrayStart(); @@ -1203,7 +1192,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeShearTimeline(JsonWriter json, Animation.ShearTimeline obj) { + private void writeShearTimeline(Animation.ShearTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1243,7 +1232,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeShearXTimeline(JsonWriter json, Animation.ShearXTimeline obj) { + private void writeShearXTimeline(Animation.ShearXTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1283,7 +1272,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeShearYTimeline(JsonWriter json, Animation.ShearYTimeline obj) { + private void writeShearYTimeline(Animation.ShearYTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1323,7 +1312,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSliderMixTimeline(JsonWriter json, Animation.SliderMixTimeline obj) { + private void writeSliderMixTimeline(Animation.SliderMixTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1363,7 +1352,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSliderTimeline(JsonWriter json, Animation.SliderTimeline obj) { + private void writeSliderTimeline(Animation.SliderTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1403,85 +1392,85 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeTimeline(JsonWriter json, Animation.Timeline obj) { + private void writeTimeline(Animation.Timeline obj) { if (obj instanceof Animation.AlphaTimeline) { - writeAlphaTimeline(json, (Animation.AlphaTimeline) obj); + writeAlphaTimeline((Animation.AlphaTimeline) obj); } else if (obj instanceof Animation.AttachmentTimeline) { - writeAttachmentTimeline(json, (Animation.AttachmentTimeline) obj); + writeAttachmentTimeline((Animation.AttachmentTimeline) obj); } else if (obj instanceof Animation.DeformTimeline) { - writeDeformTimeline(json, (Animation.DeformTimeline) obj); + writeDeformTimeline((Animation.DeformTimeline) obj); } else if (obj instanceof Animation.DrawOrderTimeline) { - writeDrawOrderTimeline(json, (Animation.DrawOrderTimeline) obj); + writeDrawOrderTimeline((Animation.DrawOrderTimeline) obj); } else if (obj instanceof Animation.EventTimeline) { - writeEventTimeline(json, (Animation.EventTimeline) obj); + writeEventTimeline((Animation.EventTimeline) obj); } else if (obj instanceof Animation.IkConstraintTimeline) { - writeIkConstraintTimeline(json, (Animation.IkConstraintTimeline) obj); + writeIkConstraintTimeline((Animation.IkConstraintTimeline) obj); } else if (obj instanceof Animation.InheritTimeline) { - writeInheritTimeline(json, (Animation.InheritTimeline) obj); + writeInheritTimeline((Animation.InheritTimeline) obj); } else if (obj instanceof Animation.PathConstraintMixTimeline) { - writePathConstraintMixTimeline(json, (Animation.PathConstraintMixTimeline) obj); + writePathConstraintMixTimeline((Animation.PathConstraintMixTimeline) obj); } else if (obj instanceof Animation.PathConstraintPositionTimeline) { - writePathConstraintPositionTimeline(json, (Animation.PathConstraintPositionTimeline) obj); + writePathConstraintPositionTimeline((Animation.PathConstraintPositionTimeline) obj); } else if (obj instanceof Animation.PathConstraintSpacingTimeline) { - writePathConstraintSpacingTimeline(json, (Animation.PathConstraintSpacingTimeline) obj); + writePathConstraintSpacingTimeline((Animation.PathConstraintSpacingTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintDampingTimeline) { - writePhysicsConstraintDampingTimeline(json, (Animation.PhysicsConstraintDampingTimeline) obj); + writePhysicsConstraintDampingTimeline((Animation.PhysicsConstraintDampingTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintGravityTimeline) { - writePhysicsConstraintGravityTimeline(json, (Animation.PhysicsConstraintGravityTimeline) obj); + writePhysicsConstraintGravityTimeline((Animation.PhysicsConstraintGravityTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintInertiaTimeline) { - writePhysicsConstraintInertiaTimeline(json, (Animation.PhysicsConstraintInertiaTimeline) obj); + writePhysicsConstraintInertiaTimeline((Animation.PhysicsConstraintInertiaTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintMassTimeline) { - writePhysicsConstraintMassTimeline(json, (Animation.PhysicsConstraintMassTimeline) obj); + writePhysicsConstraintMassTimeline((Animation.PhysicsConstraintMassTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintMixTimeline) { - writePhysicsConstraintMixTimeline(json, (Animation.PhysicsConstraintMixTimeline) obj); + writePhysicsConstraintMixTimeline((Animation.PhysicsConstraintMixTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintResetTimeline) { - writePhysicsConstraintResetTimeline(json, (Animation.PhysicsConstraintResetTimeline) obj); + writePhysicsConstraintResetTimeline((Animation.PhysicsConstraintResetTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintStrengthTimeline) { - writePhysicsConstraintStrengthTimeline(json, (Animation.PhysicsConstraintStrengthTimeline) obj); + writePhysicsConstraintStrengthTimeline((Animation.PhysicsConstraintStrengthTimeline) obj); } else if (obj instanceof Animation.PhysicsConstraintWindTimeline) { - writePhysicsConstraintWindTimeline(json, (Animation.PhysicsConstraintWindTimeline) obj); + writePhysicsConstraintWindTimeline((Animation.PhysicsConstraintWindTimeline) obj); } else if (obj instanceof Animation.RGB2Timeline) { - writeRGB2Timeline(json, (Animation.RGB2Timeline) obj); + writeRGB2Timeline((Animation.RGB2Timeline) obj); } else if (obj instanceof Animation.RGBA2Timeline) { - writeRGBA2Timeline(json, (Animation.RGBA2Timeline) obj); + writeRGBA2Timeline((Animation.RGBA2Timeline) obj); } else if (obj instanceof Animation.RGBATimeline) { - writeRGBATimeline(json, (Animation.RGBATimeline) obj); + writeRGBATimeline((Animation.RGBATimeline) obj); } else if (obj instanceof Animation.RGBTimeline) { - writeRGBTimeline(json, (Animation.RGBTimeline) obj); + writeRGBTimeline((Animation.RGBTimeline) obj); } else if (obj instanceof Animation.RotateTimeline) { - writeRotateTimeline(json, (Animation.RotateTimeline) obj); + writeRotateTimeline((Animation.RotateTimeline) obj); } else if (obj instanceof Animation.ScaleTimeline) { - writeScaleTimeline(json, (Animation.ScaleTimeline) obj); + writeScaleTimeline((Animation.ScaleTimeline) obj); } else if (obj instanceof Animation.ScaleXTimeline) { - writeScaleXTimeline(json, (Animation.ScaleXTimeline) obj); + writeScaleXTimeline((Animation.ScaleXTimeline) obj); } else if (obj instanceof Animation.ScaleYTimeline) { - writeScaleYTimeline(json, (Animation.ScaleYTimeline) obj); + writeScaleYTimeline((Animation.ScaleYTimeline) obj); } else if (obj instanceof Animation.SequenceTimeline) { - writeSequenceTimeline(json, (Animation.SequenceTimeline) obj); + writeSequenceTimeline((Animation.SequenceTimeline) obj); } else if (obj instanceof Animation.ShearTimeline) { - writeShearTimeline(json, (Animation.ShearTimeline) obj); + writeShearTimeline((Animation.ShearTimeline) obj); } else if (obj instanceof Animation.ShearXTimeline) { - writeShearXTimeline(json, (Animation.ShearXTimeline) obj); + writeShearXTimeline((Animation.ShearXTimeline) obj); } else if (obj instanceof Animation.ShearYTimeline) { - writeShearYTimeline(json, (Animation.ShearYTimeline) obj); + writeShearYTimeline((Animation.ShearYTimeline) obj); } else if (obj instanceof Animation.SliderMixTimeline) { - writeSliderMixTimeline(json, (Animation.SliderMixTimeline) obj); + writeSliderMixTimeline((Animation.SliderMixTimeline) obj); } else if (obj instanceof Animation.SliderTimeline) { - writeSliderTimeline(json, (Animation.SliderTimeline) obj); + writeSliderTimeline((Animation.SliderTimeline) obj); } else if (obj instanceof Animation.TransformConstraintTimeline) { - writeTransformConstraintTimeline(json, (Animation.TransformConstraintTimeline) obj); + writeTransformConstraintTimeline((Animation.TransformConstraintTimeline) obj); } else if (obj instanceof Animation.TranslateTimeline) { - writeTranslateTimeline(json, (Animation.TranslateTimeline) obj); + writeTranslateTimeline((Animation.TranslateTimeline) obj); } else if (obj instanceof Animation.TranslateXTimeline) { - writeTranslateXTimeline(json, (Animation.TranslateXTimeline) obj); + writeTranslateXTimeline((Animation.TranslateXTimeline) obj); } else if (obj instanceof Animation.TranslateYTimeline) { - writeTranslateYTimeline(json, (Animation.TranslateYTimeline) obj); + writeTranslateYTimeline((Animation.TranslateYTimeline) obj); } else { throw new RuntimeException("Unknown Timeline type: " + obj.getClass().getName()); } } - private void writeTransformConstraintTimeline(JsonWriter json, Animation.TransformConstraintTimeline obj) { + private void writeTransformConstraintTimeline(Animation.TransformConstraintTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1521,7 +1510,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeTranslateTimeline(JsonWriter json, Animation.TranslateTimeline obj) { + private void writeTranslateTimeline(Animation.TranslateTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1561,7 +1550,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeTranslateXTimeline(JsonWriter json, Animation.TranslateXTimeline obj) { + private void writeTranslateXTimeline(Animation.TranslateXTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1601,7 +1590,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeTranslateYTimeline(JsonWriter json, Animation.TranslateYTimeline obj) { + private void writeTranslateYTimeline(Animation.TranslateYTimeline obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1641,7 +1630,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeAnimationState(JsonWriter json, AnimationState obj) { + private void writeAnimationState(AnimationState obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1656,19 +1645,19 @@ public class SkeletonSerializer { json.writeValue(obj.getTimeScale()); json.writeName("data"); - writeAnimationStateData(json, obj.getData()); + writeAnimationStateData(obj.getData()); json.writeName("tracks"); json.writeArrayStart(); for (TrackEntry item : obj.getTracks()) { - writeTrackEntry(json, item); + writeTrackEntry(item); } json.writeArrayEnd(); json.writeObjectEnd(); } - private void writeTrackEntry(JsonWriter json, AnimationState.TrackEntry obj) { + private void writeTrackEntry(AnimationState.TrackEntry obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1683,7 +1672,7 @@ public class SkeletonSerializer { json.writeValue(obj.getTrackIndex()); json.writeName("animation"); - writeAnimation(json, obj.getAnimation()); + writeAnimation(obj.getAnimation()); json.writeName("loop"); json.writeValue(obj.getLoop()); @@ -1714,7 +1703,6 @@ public class SkeletonSerializer { json.writeName("timeScale"); json.writeValue(obj.getTimeScale()); - // Skipping excluded property: getListener() json.writeName("alpha"); json.writeValue(obj.getAlpha()); @@ -1735,14 +1723,14 @@ public class SkeletonSerializer { if (obj.getNext() == null) { json.writeNull(); } else { - writeTrackEntry(json, obj.getNext()); + writeTrackEntry(obj.getNext()); } json.writeName("previous"); if (obj.getPrevious() == null) { json.writeNull(); } else { - writeTrackEntry(json, obj.getPrevious()); + writeTrackEntry(obj.getPrevious()); } json.writeName("mixTime"); @@ -1758,14 +1746,14 @@ public class SkeletonSerializer { if (obj.getMixingFrom() == null) { json.writeNull(); } else { - writeTrackEntry(json, obj.getMixingFrom()); + writeTrackEntry(obj.getMixingFrom()); } json.writeName("mixingTo"); if (obj.getMixingTo() == null) { json.writeNull(); } else { - writeTrackEntry(json, obj.getMixingTo()); + writeTrackEntry(obj.getMixingTo()); } json.writeName("holdPrevious"); @@ -1780,7 +1768,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeAnimationStateData(JsonWriter json, AnimationStateData obj) { + private void writeAnimationStateData(AnimationStateData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1792,7 +1780,7 @@ public class SkeletonSerializer { json.writeValue("AnimationStateData"); json.writeName("skeletonData"); - writeSkeletonData(json, obj.getSkeletonData()); + writeSkeletonData(obj.getSkeletonData()); json.writeName("defaultMix"); json.writeValue(obj.getDefaultMix()); @@ -1800,25 +1788,25 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeAttachment(JsonWriter json, Attachment obj) { + private void writeAttachment(Attachment obj) { if (obj instanceof BoundingBoxAttachment) { - writeBoundingBoxAttachment(json, (BoundingBoxAttachment) obj); + writeBoundingBoxAttachment((BoundingBoxAttachment) obj); } else if (obj instanceof ClippingAttachment) { - writeClippingAttachment(json, (ClippingAttachment) obj); + writeClippingAttachment((ClippingAttachment) obj); } else if (obj instanceof MeshAttachment) { - writeMeshAttachment(json, (MeshAttachment) obj); + writeMeshAttachment((MeshAttachment) obj); } else if (obj instanceof PathAttachment) { - writePathAttachment(json, (PathAttachment) obj); + writePathAttachment((PathAttachment) obj); } else if (obj instanceof PointAttachment) { - writePointAttachment(json, (PointAttachment) obj); + writePointAttachment((PointAttachment) obj); } else if (obj instanceof RegionAttachment) { - writeRegionAttachment(json, (RegionAttachment) obj); + writeRegionAttachment((RegionAttachment) obj); } else { throw new RuntimeException("Unknown Attachment type: " + obj.getClass().getName()); } } - private void writeBone(JsonWriter json, Bone obj) { + private void writeBone(Bone obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1833,29 +1821,29 @@ public class SkeletonSerializer { if (obj.getParent() == null) { json.writeNull(); } else { - writeBone(json, obj.getParent()); + writeBone(obj.getParent()); } json.writeName("children"); json.writeArrayStart(); for (Bone item : obj.getChildren()) { - writeBone(json, item); + writeBone(item); } json.writeArrayEnd(); json.writeName("data"); - writeBoneData(json, obj.getData()); + writeBoneData(obj.getData()); json.writeName("pose"); - writeBoneLocal(json, obj.getPose()); + writeBoneLocal(obj.getPose()); json.writeName("appliedPose"); - writeBonePose(json, obj.getAppliedPose()); + writeBonePose(obj.getAppliedPose()); json.writeObjectEnd(); } - private void writeBoneData(JsonWriter json, BoneData obj) { + private void writeBoneData(BoneData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1873,14 +1861,14 @@ public class SkeletonSerializer { if (obj.getParent() == null) { json.writeNull(); } else { - writeBoneData(json, obj.getParent()); + writeBoneData(obj.getParent()); } json.writeName("length"); json.writeValue(obj.getLength()); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("icon"); json.writeValue(obj.getIcon()); @@ -1892,7 +1880,7 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("setupPose"); - writeBoneLocal(json, obj.getSetupPose()); + writeBoneLocal(obj.getSetupPose()); json.writeName("skinRequired"); json.writeValue(obj.getSkinRequired()); @@ -1900,7 +1888,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeBoneLocal(JsonWriter json, BoneLocal obj) { + private void writeBoneLocal(BoneLocal obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -1938,7 +1926,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeBonePose(JsonWriter json, BonePose obj) { + private void writeBonePose(BonePose obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2006,7 +1994,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeBoundingBoxAttachment(JsonWriter json, BoundingBoxAttachment obj) { + private void writeBoundingBoxAttachment(BoundingBoxAttachment obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2018,7 +2006,7 @@ public class SkeletonSerializer { json.writeValue("BoundingBoxAttachment"); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("bones"); if (obj.getBones() == null) { @@ -2045,7 +2033,7 @@ public class SkeletonSerializer { if (obj.getTimelineAttachment() == null) { json.writeNull(); } else { - writeAttachment(json, obj.getTimelineAttachment()); + writeAttachment(obj.getTimelineAttachment()); } json.writeName("id"); @@ -2057,7 +2045,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeClippingAttachment(JsonWriter json, ClippingAttachment obj) { + private void writeClippingAttachment(ClippingAttachment obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2072,11 +2060,11 @@ public class SkeletonSerializer { if (obj.getEndSlot() == null) { json.writeNull(); } else { - writeSlotData(json, obj.getEndSlot()); + writeSlotData(obj.getEndSlot()); } json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("bones"); if (obj.getBones() == null) { @@ -2103,7 +2091,7 @@ public class SkeletonSerializer { if (obj.getTimelineAttachment() == null) { json.writeNull(); } else { - writeAttachment(json, obj.getTimelineAttachment()); + writeAttachment(obj.getTimelineAttachment()); } json.writeName("id"); @@ -2115,39 +2103,39 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeConstraint(JsonWriter json, Constraint obj) { + private void writeConstraint(Constraint obj) { if (obj instanceof IkConstraint) { - writeIkConstraint(json, (IkConstraint) obj); + writeIkConstraint((IkConstraint) obj); } else if (obj instanceof PathConstraint) { - writePathConstraint(json, (PathConstraint) obj); + writePathConstraint((PathConstraint) obj); } else if (obj instanceof PhysicsConstraint) { - writePhysicsConstraint(json, (PhysicsConstraint) obj); + writePhysicsConstraint((PhysicsConstraint) obj); } else if (obj instanceof Slider) { - writeSlider(json, (Slider) obj); + writeSlider((Slider) obj); } else if (obj instanceof TransformConstraint) { - writeTransformConstraint(json, (TransformConstraint) obj); + writeTransformConstraint((TransformConstraint) obj); } else { throw new RuntimeException("Unknown Constraint type: " + obj.getClass().getName()); } } - private void writeConstraintData(JsonWriter json, ConstraintData obj) { + private void writeConstraintData(ConstraintData obj) { if (obj instanceof IkConstraintData) { - writeIkConstraintData(json, (IkConstraintData) obj); + writeIkConstraintData((IkConstraintData) obj); } else if (obj instanceof PathConstraintData) { - writePathConstraintData(json, (PathConstraintData) obj); + writePathConstraintData((PathConstraintData) obj); } else if (obj instanceof PhysicsConstraintData) { - writePhysicsConstraintData(json, (PhysicsConstraintData) obj); + writePhysicsConstraintData((PhysicsConstraintData) obj); } else if (obj instanceof SliderData) { - writeSliderData(json, (SliderData) obj); + writeSliderData((SliderData) obj); } else if (obj instanceof TransformConstraintData) { - writeTransformConstraintData(json, (TransformConstraintData) obj); + writeTransformConstraintData((TransformConstraintData) obj); } else { throw new RuntimeException("Unknown ConstraintData type: " + obj.getClass().getName()); } } - private void writeEvent(JsonWriter json, Event obj) { + private void writeEvent(Event obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2177,12 +2165,12 @@ public class SkeletonSerializer { json.writeValue(obj.getTime()); json.writeName("data"); - writeEventData(json, obj.getData()); + writeEventData(obj.getData()); json.writeObjectEnd(); } - private void writeEventData(JsonWriter json, EventData obj) { + private void writeEventData(EventData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2217,7 +2205,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeIkConstraint(JsonWriter json, IkConstraint obj) { + private void writeIkConstraint(IkConstraint obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2231,26 +2219,26 @@ public class SkeletonSerializer { json.writeName("bones"); json.writeArrayStart(); for (BonePose item : obj.getBones()) { - writeBonePose(json, item); + writeBonePose(item); } json.writeArrayEnd(); json.writeName("target"); - writeBone(json, obj.getTarget()); + writeBone(obj.getTarget()); json.writeName("data"); - writeIkConstraintData(json, obj.getData()); + writeIkConstraintData(obj.getData()); json.writeName("pose"); - writeIkConstraintPose(json, obj.getPose()); + writeIkConstraintPose(obj.getPose()); json.writeName("appliedPose"); - writeIkConstraintPose(json, obj.getAppliedPose()); + writeIkConstraintPose(obj.getAppliedPose()); json.writeObjectEnd(); } - private void writeIkConstraintData(JsonWriter json, IkConstraintData obj) { + private void writeIkConstraintData(IkConstraintData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2264,12 +2252,12 @@ public class SkeletonSerializer { json.writeName("bones"); json.writeArrayStart(); for (BoneData item : obj.getBones()) { - writeBoneData(json, item); + writeBoneData(item); } json.writeArrayEnd(); json.writeName("target"); - writeBoneData(json, obj.getTarget()); + writeBoneData(obj.getTarget()); json.writeName("uniform"); json.writeValue(obj.getUniform()); @@ -2278,7 +2266,7 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("setupPose"); - writeIkConstraintPose(json, obj.getSetupPose()); + writeIkConstraintPose(obj.getSetupPose()); json.writeName("skinRequired"); json.writeValue(obj.getSkinRequired()); @@ -2286,7 +2274,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeIkConstraintPose(JsonWriter json, IkConstraintPose obj) { + private void writeIkConstraintPose(IkConstraintPose obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2315,7 +2303,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeMeshAttachment(JsonWriter json, MeshAttachment obj) { + private void writeMeshAttachment(MeshAttachment obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2327,7 +2315,11 @@ public class SkeletonSerializer { json.writeValue("MeshAttachment"); json.writeName("region"); - writeTextureRegion(json, obj.getRegion()); + if (obj.getRegion() == null) { + json.writeNull(); + } else { + writeTextureRegion(obj.getRegion()); + } json.writeName("triangles"); json.writeArrayStart(); @@ -2351,7 +2343,7 @@ public class SkeletonSerializer { json.writeArrayEnd(); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("path"); json.writeValue(obj.getPath()); @@ -2380,14 +2372,14 @@ public class SkeletonSerializer { if (obj.getSequence() == null) { json.writeNull(); } else { - writeSequence(json, obj.getSequence()); + writeSequence(obj.getSequence()); } json.writeName("parentMesh"); if (obj.getParentMesh() == null) { json.writeNull(); } else { - writeMeshAttachment(json, obj.getParentMesh()); + writeMeshAttachment(obj.getParentMesh()); } json.writeName("bones"); @@ -2415,7 +2407,7 @@ public class SkeletonSerializer { if (obj.getTimelineAttachment() == null) { json.writeNull(); } else { - writeAttachment(json, obj.getTimelineAttachment()); + writeAttachment(obj.getTimelineAttachment()); } json.writeName("id"); @@ -2427,7 +2419,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePathAttachment(JsonWriter json, PathAttachment obj) { + private void writePathAttachment(PathAttachment obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2452,7 +2444,7 @@ public class SkeletonSerializer { json.writeArrayEnd(); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("bones"); if (obj.getBones() == null) { @@ -2479,7 +2471,7 @@ public class SkeletonSerializer { if (obj.getTimelineAttachment() == null) { json.writeNull(); } else { - writeAttachment(json, obj.getTimelineAttachment()); + writeAttachment(obj.getTimelineAttachment()); } json.writeName("id"); @@ -2491,7 +2483,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePathConstraint(JsonWriter json, PathConstraint obj) { + private void writePathConstraint(PathConstraint obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2505,26 +2497,26 @@ public class SkeletonSerializer { json.writeName("bones"); json.writeArrayStart(); for (BonePose item : obj.getBones()) { - writeBonePose(json, item); + writeBonePose(item); } json.writeArrayEnd(); json.writeName("slot"); - writeSlot(json, obj.getSlot()); + writeSlot(obj.getSlot()); json.writeName("data"); - writePathConstraintData(json, obj.getData()); + writePathConstraintData(obj.getData()); json.writeName("pose"); - writePathConstraintPose(json, obj.getPose()); + writePathConstraintPose(obj.getPose()); json.writeName("appliedPose"); - writePathConstraintPose(json, obj.getAppliedPose()); + writePathConstraintPose(obj.getAppliedPose()); json.writeObjectEnd(); } - private void writePathConstraintData(JsonWriter json, PathConstraintData obj) { + private void writePathConstraintData(PathConstraintData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2538,12 +2530,12 @@ public class SkeletonSerializer { json.writeName("bones"); json.writeArrayStart(); for (BoneData item : obj.getBones()) { - writeBoneData(json, item); + writeBoneData(item); } json.writeArrayEnd(); json.writeName("slot"); - writeSlotData(json, obj.getSlot()); + writeSlotData(obj.getSlot()); json.writeName("positionMode"); json.writeValue(obj.getPositionMode().name()); @@ -2561,7 +2553,7 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("setupPose"); - writePathConstraintPose(json, obj.getSetupPose()); + writePathConstraintPose(obj.getSetupPose()); json.writeName("skinRequired"); json.writeValue(obj.getSkinRequired()); @@ -2569,7 +2561,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePathConstraintPose(JsonWriter json, PathConstraintPose obj) { + private void writePathConstraintPose(PathConstraintPose obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2598,7 +2590,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraint(JsonWriter json, PhysicsConstraint obj) { + private void writePhysicsConstraint(PhysicsConstraint obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2610,21 +2602,21 @@ public class SkeletonSerializer { json.writeValue("PhysicsConstraint"); json.writeName("bone"); - writeBonePose(json, obj.getBone()); + writeBonePose(obj.getBone()); json.writeName("data"); - writePhysicsConstraintData(json, obj.getData()); + writePhysicsConstraintData(obj.getData()); json.writeName("pose"); - writePhysicsConstraintPose(json, obj.getPose()); + writePhysicsConstraintPose(obj.getPose()); json.writeName("appliedPose"); - writePhysicsConstraintPose(json, obj.getAppliedPose()); + writePhysicsConstraintPose(obj.getAppliedPose()); json.writeObjectEnd(); } - private void writePhysicsConstraintData(JsonWriter json, PhysicsConstraintData obj) { + private void writePhysicsConstraintData(PhysicsConstraintData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2636,7 +2628,7 @@ public class SkeletonSerializer { json.writeValue("PhysicsConstraintData"); json.writeName("bone"); - writeBoneData(json, obj.getBone()); + writeBoneData(obj.getBone()); json.writeName("step"); json.writeValue(obj.getStep()); @@ -2684,7 +2676,7 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("setupPose"); - writePhysicsConstraintPose(json, obj.getSetupPose()); + writePhysicsConstraintPose(obj.getSetupPose()); json.writeName("skinRequired"); json.writeValue(obj.getSkinRequired()); @@ -2692,7 +2684,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePhysicsConstraintPose(JsonWriter json, PhysicsConstraintPose obj) { + private void writePhysicsConstraintPose(PhysicsConstraintPose obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2727,7 +2719,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writePointAttachment(JsonWriter json, PointAttachment obj) { + private void writePointAttachment(PointAttachment obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2748,7 +2740,7 @@ public class SkeletonSerializer { json.writeValue(obj.getRotation()); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("name"); json.writeValue(obj.getName()); @@ -2756,7 +2748,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeRegionAttachment(JsonWriter json, RegionAttachment obj) { + private void writeRegionAttachment(RegionAttachment obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2768,7 +2760,11 @@ public class SkeletonSerializer { json.writeValue("RegionAttachment"); json.writeName("region"); - writeTextureRegion(json, obj.getRegion()); + if (obj.getRegion() == null) { + json.writeNull(); + } else { + writeTextureRegion(obj.getRegion()); + } json.writeName("offset"); json.writeArrayStart(); @@ -2806,7 +2802,7 @@ public class SkeletonSerializer { json.writeValue(obj.getHeight()); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("path"); json.writeValue(obj.getPath()); @@ -2815,7 +2811,7 @@ public class SkeletonSerializer { if (obj.getSequence() == null) { json.writeNull(); } else { - writeSequence(json, obj.getSequence()); + writeSequence(obj.getSequence()); } json.writeName("name"); @@ -2824,7 +2820,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSequence(JsonWriter json, Sequence obj) { + private void writeSequence(Sequence obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2847,7 +2843,7 @@ public class SkeletonSerializer { json.writeName("regions"); json.writeArrayStart(); for (TextureRegion item : obj.getRegions()) { - writeTextureRegion(json, item); + writeTextureRegion(item); } json.writeArrayEnd(); @@ -2857,7 +2853,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSkeleton(JsonWriter json, Skeleton obj) { + private void writeSkeleton(Skeleton obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2869,36 +2865,36 @@ public class SkeletonSerializer { json.writeValue("Skeleton"); json.writeName("data"); - writeSkeletonData(json, obj.getData()); + writeSkeletonData(obj.getData()); json.writeName("bones"); json.writeArrayStart(); for (Bone item : obj.getBones()) { - writeBone(json, item); + writeBone(item); } json.writeArrayEnd(); json.writeName("updateCache"); json.writeArrayStart(); for (Update item : obj.getUpdateCache()) { - writeUpdate(json, item); + writeUpdate(item); } json.writeArrayEnd(); json.writeName("rootBone"); - writeBone(json, obj.getRootBone()); + writeBone(obj.getRootBone()); json.writeName("slots"); json.writeArrayStart(); for (Slot item : obj.getSlots()) { - writeSlot(json, item); + writeSlot(item); } json.writeArrayEnd(); json.writeName("drawOrder"); json.writeArrayStart(); for (Slot item : obj.getDrawOrder()) { - writeSlot(json, item); + writeSlot(item); } json.writeArrayEnd(); @@ -2906,25 +2902,25 @@ public class SkeletonSerializer { if (obj.getSkin() == null) { json.writeNull(); } else { - writeSkin(json, obj.getSkin()); + writeSkin(obj.getSkin()); } json.writeName("constraints"); json.writeArrayStart(); for (Constraint item : obj.getConstraints()) { - writeConstraint(json, item); + writeConstraint(item); } json.writeArrayEnd(); json.writeName("physicsConstraints"); json.writeArrayStart(); for (PhysicsConstraint item : obj.getPhysicsConstraints()) { - writePhysicsConstraint(json, item); + writePhysicsConstraint(item); } json.writeArrayEnd(); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("scaleX"); json.writeValue(obj.getScaleX()); @@ -2956,7 +2952,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSkeletonData(JsonWriter json, SkeletonData obj) { + private void writeSkeletonData(SkeletonData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -2970,14 +2966,14 @@ public class SkeletonSerializer { json.writeName("bones"); json.writeArrayStart(); for (BoneData item : obj.getBones()) { - writeBoneData(json, item); + writeBoneData(item); } json.writeArrayEnd(); json.writeName("slots"); json.writeArrayStart(); for (SlotData item : obj.getSlots()) { - writeSlotData(json, item); + writeSlotData(item); } json.writeArrayEnd(); @@ -2985,34 +2981,34 @@ public class SkeletonSerializer { if (obj.getDefaultSkin() == null) { json.writeNull(); } else { - writeSkin(json, obj.getDefaultSkin()); + writeSkin(obj.getDefaultSkin()); } json.writeName("skins"); json.writeArrayStart(); for (Skin item : obj.getSkins()) { - writeSkin(json, item); + writeSkin(item); } json.writeArrayEnd(); json.writeName("events"); json.writeArrayStart(); for (EventData item : obj.getEvents()) { - writeEventData(json, item); + writeEventData(item); } json.writeArrayEnd(); json.writeName("animations"); json.writeArrayStart(); for (Animation item : obj.getAnimations()) { - writeAnimation(json, item); + writeAnimation(item); } json.writeArrayEnd(); json.writeName("constraints"); json.writeArrayStart(); for (ConstraintData item : obj.getConstraints()) { - writeConstraintData(json, item); + writeConstraintData(item); } json.writeArrayEnd(); @@ -3052,7 +3048,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSkin(JsonWriter json, Skin obj) { + private void writeSkin(Skin obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3066,21 +3062,21 @@ public class SkeletonSerializer { json.writeName("attachments"); json.writeArrayStart(); for (SkinEntry item : obj.getAttachments()) { - writeSkinEntry(json, item); + writeSkinEntry(item); } json.writeArrayEnd(); json.writeName("bones"); json.writeArrayStart(); for (BoneData item : obj.getBones()) { - writeBoneData(json, item); + writeBoneData(item); } json.writeArrayEnd(); json.writeName("constraints"); json.writeArrayStart(); for (ConstraintData item : obj.getConstraints()) { - writeConstraintData(json, item); + writeConstraintData(item); } json.writeArrayEnd(); @@ -3088,12 +3084,12 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeObjectEnd(); } - private void writeSkinEntry(JsonWriter json, Skin.SkinEntry obj) { + private void writeSkinEntry(Skin.SkinEntry obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3111,12 +3107,12 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("attachment"); - writeAttachment(json, obj.getAttachment()); + writeAttachment(obj.getAttachment()); json.writeObjectEnd(); } - private void writeSlider(JsonWriter json, Slider obj) { + private void writeSlider(Slider obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3128,21 +3124,21 @@ public class SkeletonSerializer { json.writeValue("Slider"); json.writeName("bone"); - writeBone(json, obj.getBone()); + writeBone(obj.getBone()); json.writeName("data"); - writeSliderData(json, obj.getData()); + writeSliderData(obj.getData()); json.writeName("pose"); - writeSliderPose(json, obj.getPose()); + writeSliderPose(obj.getPose()); json.writeName("appliedPose"); - writeSliderPose(json, obj.getAppliedPose()); + writeSliderPose(obj.getAppliedPose()); json.writeObjectEnd(); } - private void writeSliderData(JsonWriter json, SliderData obj) { + private void writeSliderData(SliderData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3154,7 +3150,7 @@ public class SkeletonSerializer { json.writeValue("SliderData"); json.writeName("animation"); - writeAnimation(json, obj.getAnimation()); + writeAnimation(obj.getAnimation()); json.writeName("additive"); json.writeValue(obj.getAdditive()); @@ -3166,14 +3162,14 @@ public class SkeletonSerializer { if (obj.getBone() == null) { json.writeNull(); } else { - writeBoneData(json, obj.getBone()); + writeBoneData(obj.getBone()); } json.writeName("property"); if (obj.getProperty() == null) { json.writeNull(); } else { - writeFromProperty(json, obj.getProperty()); + writeFromProperty(obj.getProperty()); } json.writeName("offset"); @@ -3189,7 +3185,7 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("setupPose"); - writeSliderPose(json, obj.getSetupPose()); + writeSliderPose(obj.getSetupPose()); json.writeName("skinRequired"); json.writeValue(obj.getSkinRequired()); @@ -3197,7 +3193,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSliderPose(JsonWriter json, SliderPose obj) { + private void writeSliderPose(SliderPose obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3217,7 +3213,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSlot(JsonWriter json, Slot obj) { + private void writeSlot(Slot obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3229,21 +3225,21 @@ public class SkeletonSerializer { json.writeValue("Slot"); json.writeName("bone"); - writeBone(json, obj.getBone()); + writeBone(obj.getBone()); json.writeName("data"); - writeSlotData(json, obj.getData()); + writeSlotData(obj.getData()); json.writeName("pose"); - writeSlotPose(json, obj.getPose()); + writeSlotPose(obj.getPose()); json.writeName("appliedPose"); - writeSlotPose(json, obj.getAppliedPose()); + writeSlotPose(obj.getAppliedPose()); json.writeObjectEnd(); } - private void writeSlotData(JsonWriter json, SlotData obj) { + private void writeSlotData(SlotData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3258,7 +3254,7 @@ public class SkeletonSerializer { json.writeValue(obj.getIndex()); json.writeName("boneData"); - writeBoneData(json, obj.getBoneData()); + writeBoneData(obj.getBoneData()); json.writeName("attachmentName"); json.writeValue(obj.getAttachmentName()); @@ -3273,7 +3269,7 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("setupPose"); - writeSlotPose(json, obj.getSetupPose()); + writeSlotPose(obj.getSetupPose()); json.writeName("skinRequired"); json.writeValue(obj.getSkinRequired()); @@ -3281,7 +3277,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeSlotPose(JsonWriter json, SlotPose obj) { + private void writeSlotPose(SlotPose obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3293,32 +3289,32 @@ public class SkeletonSerializer { json.writeValue("SlotPose"); json.writeName("color"); - writeColor(json, obj.getColor()); + writeColor(obj.getColor()); json.writeName("darkColor"); - writeColor(json, obj.getDarkColor()); + if (obj.getDarkColor() == null) { + json.writeNull(); + } else { + writeColor(obj.getDarkColor()); + } json.writeName("attachment"); if (obj.getAttachment() == null) { json.writeNull(); } else { - writeAttachment(json, obj.getAttachment()); + writeAttachment(obj.getAttachment()); } json.writeName("sequenceIndex"); json.writeValue(obj.getSequenceIndex()); json.writeName("deform"); - json.writeArrayStart(); - for (int i = 0; i < obj.getDeform().size; i++) { - json.writeValue(obj.getDeform().get(i)); - } - json.writeArrayEnd(); + writeFloatArray(obj.getDeform()); json.writeObjectEnd(); } - private void writeTransformConstraint(JsonWriter json, TransformConstraint obj) { + private void writeTransformConstraint(TransformConstraint obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3332,26 +3328,26 @@ public class SkeletonSerializer { json.writeName("bones"); json.writeArrayStart(); for (BonePose item : obj.getBones()) { - writeBonePose(json, item); + writeBonePose(item); } json.writeArrayEnd(); json.writeName("source"); - writeBone(json, obj.getSource()); + writeBone(obj.getSource()); json.writeName("data"); - writeTransformConstraintData(json, obj.getData()); + writeTransformConstraintData(obj.getData()); json.writeName("pose"); - writeTransformConstraintPose(json, obj.getPose()); + writeTransformConstraintPose(obj.getPose()); json.writeName("appliedPose"); - writeTransformConstraintPose(json, obj.getAppliedPose()); + writeTransformConstraintPose(obj.getAppliedPose()); json.writeObjectEnd(); } - private void writeTransformConstraintData(JsonWriter json, TransformConstraintData obj) { + private void writeTransformConstraintData(TransformConstraintData obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3365,12 +3361,12 @@ public class SkeletonSerializer { json.writeName("bones"); json.writeArrayStart(); for (BoneData item : obj.getBones()) { - writeBoneData(json, item); + writeBoneData(item); } json.writeArrayEnd(); json.writeName("source"); - writeBoneData(json, obj.getSource()); + writeBoneData(obj.getSource()); json.writeName("offsetRotation"); json.writeValue(obj.getOffsetRotation()); @@ -3405,7 +3401,7 @@ public class SkeletonSerializer { json.writeName("properties"); json.writeArrayStart(); for (FromProperty item : obj.getProperties()) { - writeFromProperty(json, item); + writeFromProperty(item); } json.writeArrayEnd(); @@ -3413,7 +3409,7 @@ public class SkeletonSerializer { json.writeValue(obj.getName()); json.writeName("setupPose"); - writeTransformConstraintPose(json, obj.getSetupPose()); + writeTransformConstraintPose(obj.getSetupPose()); json.writeName("skinRequired"); json.writeValue(obj.getSkinRequired()); @@ -3421,25 +3417,25 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeFromProperty(JsonWriter json, TransformConstraintData.FromProperty obj) { + private void writeFromProperty(TransformConstraintData.FromProperty obj) { if (obj instanceof TransformConstraintData.FromRotate) { - writeFromRotate(json, (TransformConstraintData.FromRotate) obj); + writeFromRotate((TransformConstraintData.FromRotate) obj); } else if (obj instanceof TransformConstraintData.FromScaleX) { - writeFromScaleX(json, (TransformConstraintData.FromScaleX) obj); + writeFromScaleX((TransformConstraintData.FromScaleX) obj); } else if (obj instanceof TransformConstraintData.FromScaleY) { - writeFromScaleY(json, (TransformConstraintData.FromScaleY) obj); + writeFromScaleY((TransformConstraintData.FromScaleY) obj); } else if (obj instanceof TransformConstraintData.FromShearY) { - writeFromShearY(json, (TransformConstraintData.FromShearY) obj); + writeFromShearY((TransformConstraintData.FromShearY) obj); } else if (obj instanceof TransformConstraintData.FromX) { - writeFromX(json, (TransformConstraintData.FromX) obj); + writeFromX((TransformConstraintData.FromX) obj); } else if (obj instanceof TransformConstraintData.FromY) { - writeFromY(json, (TransformConstraintData.FromY) obj); + writeFromY((TransformConstraintData.FromY) obj); } else { throw new RuntimeException("Unknown FromProperty type: " + obj.getClass().getName()); } } - private void writeFromRotate(JsonWriter json, TransformConstraintData.FromRotate obj) { + private void writeFromRotate(TransformConstraintData.FromRotate obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3456,14 +3452,14 @@ public class SkeletonSerializer { json.writeName("to"); json.writeArrayStart(); for (ToProperty item : obj.to) { - writeToProperty(json, item); + writeToProperty(item); } json.writeArrayEnd(); json.writeObjectEnd(); } - private void writeFromScaleX(JsonWriter json, TransformConstraintData.FromScaleX obj) { + private void writeFromScaleX(TransformConstraintData.FromScaleX obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3480,14 +3476,14 @@ public class SkeletonSerializer { json.writeName("to"); json.writeArrayStart(); for (ToProperty item : obj.to) { - writeToProperty(json, item); + writeToProperty(item); } json.writeArrayEnd(); json.writeObjectEnd(); } - private void writeFromScaleY(JsonWriter json, TransformConstraintData.FromScaleY obj) { + private void writeFromScaleY(TransformConstraintData.FromScaleY obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3504,14 +3500,14 @@ public class SkeletonSerializer { json.writeName("to"); json.writeArrayStart(); for (ToProperty item : obj.to) { - writeToProperty(json, item); + writeToProperty(item); } json.writeArrayEnd(); json.writeObjectEnd(); } - private void writeFromShearY(JsonWriter json, TransformConstraintData.FromShearY obj) { + private void writeFromShearY(TransformConstraintData.FromShearY obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3528,14 +3524,14 @@ public class SkeletonSerializer { json.writeName("to"); json.writeArrayStart(); for (ToProperty item : obj.to) { - writeToProperty(json, item); + writeToProperty(item); } json.writeArrayEnd(); json.writeObjectEnd(); } - private void writeFromX(JsonWriter json, TransformConstraintData.FromX obj) { + private void writeFromX(TransformConstraintData.FromX obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3552,14 +3548,14 @@ public class SkeletonSerializer { json.writeName("to"); json.writeArrayStart(); for (ToProperty item : obj.to) { - writeToProperty(json, item); + writeToProperty(item); } json.writeArrayEnd(); json.writeObjectEnd(); } - private void writeFromY(JsonWriter json, TransformConstraintData.FromY obj) { + private void writeFromY(TransformConstraintData.FromY obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3576,32 +3572,32 @@ public class SkeletonSerializer { json.writeName("to"); json.writeArrayStart(); for (ToProperty item : obj.to) { - writeToProperty(json, item); + writeToProperty(item); } json.writeArrayEnd(); json.writeObjectEnd(); } - private void writeToProperty(JsonWriter json, TransformConstraintData.ToProperty obj) { + private void writeToProperty(TransformConstraintData.ToProperty obj) { if (obj instanceof TransformConstraintData.ToRotate) { - writeToRotate(json, (TransformConstraintData.ToRotate) obj); + writeToRotate((TransformConstraintData.ToRotate) obj); } else if (obj instanceof TransformConstraintData.ToScaleX) { - writeToScaleX(json, (TransformConstraintData.ToScaleX) obj); + writeToScaleX((TransformConstraintData.ToScaleX) obj); } else if (obj instanceof TransformConstraintData.ToScaleY) { - writeToScaleY(json, (TransformConstraintData.ToScaleY) obj); + writeToScaleY((TransformConstraintData.ToScaleY) obj); } else if (obj instanceof TransformConstraintData.ToShearY) { - writeToShearY(json, (TransformConstraintData.ToShearY) obj); + writeToShearY((TransformConstraintData.ToShearY) obj); } else if (obj instanceof TransformConstraintData.ToX) { - writeToX(json, (TransformConstraintData.ToX) obj); + writeToX((TransformConstraintData.ToX) obj); } else if (obj instanceof TransformConstraintData.ToY) { - writeToY(json, (TransformConstraintData.ToY) obj); + writeToY((TransformConstraintData.ToY) obj); } else { throw new RuntimeException("Unknown ToProperty type: " + obj.getClass().getName()); } } - private void writeToRotate(JsonWriter json, TransformConstraintData.ToRotate obj) { + private void writeToRotate(TransformConstraintData.ToRotate obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3624,7 +3620,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeToScaleX(JsonWriter json, TransformConstraintData.ToScaleX obj) { + private void writeToScaleX(TransformConstraintData.ToScaleX obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3647,7 +3643,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeToScaleY(JsonWriter json, TransformConstraintData.ToScaleY obj) { + private void writeToScaleY(TransformConstraintData.ToScaleY obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3670,7 +3666,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeToShearY(JsonWriter json, TransformConstraintData.ToShearY obj) { + private void writeToShearY(TransformConstraintData.ToShearY obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3693,7 +3689,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeToX(JsonWriter json, TransformConstraintData.ToX obj) { + private void writeToX(TransformConstraintData.ToX obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3716,7 +3712,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeToY(JsonWriter json, TransformConstraintData.ToY obj) { + private void writeToY(TransformConstraintData.ToY obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3739,7 +3735,7 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeTransformConstraintPose(JsonWriter json, TransformConstraintPose obj) { + private void writeTransformConstraintPose(TransformConstraintPose obj) { if (visitedObjects.contains(obj)) { json.writeValue(""); return; @@ -3771,73 +3767,97 @@ public class SkeletonSerializer { json.writeObjectEnd(); } - private void writeUpdate(JsonWriter json, Update obj) { + private void writeUpdate(Update obj) { if (obj instanceof BonePose) { - writeBonePose(json, (BonePose) obj); + writeBonePose((BonePose) obj); } else if (obj instanceof IkConstraint) { - writeIkConstraint(json, (IkConstraint) obj); + writeIkConstraint((IkConstraint) obj); } else if (obj instanceof PathConstraint) { - writePathConstraint(json, (PathConstraint) obj); + writePathConstraint((PathConstraint) obj); } else if (obj instanceof PhysicsConstraint) { - writePhysicsConstraint(json, (PhysicsConstraint) obj); + writePhysicsConstraint((PhysicsConstraint) obj); } else if (obj instanceof Slider) { - writeSlider(json, (Slider) obj); + writeSlider((Slider) obj); } else if (obj instanceof TransformConstraint) { - writeTransformConstraint(json, (TransformConstraint) obj); + writeTransformConstraint((TransformConstraint) obj); } else { throw new RuntimeException("Unknown Update type: " + obj.getClass().getName()); } } - private void writeVertexAttachment(JsonWriter json, VertexAttachment obj) { + private void writeVertexAttachment(VertexAttachment obj) { if (obj instanceof BoundingBoxAttachment) { - writeBoundingBoxAttachment(json, (BoundingBoxAttachment) obj); + writeBoundingBoxAttachment((BoundingBoxAttachment) obj); } else if (obj instanceof ClippingAttachment) { - writeClippingAttachment(json, (ClippingAttachment) obj); + writeClippingAttachment((ClippingAttachment) obj); } else if (obj instanceof MeshAttachment) { - writeMeshAttachment(json, (MeshAttachment) obj); + writeMeshAttachment((MeshAttachment) obj); } else if (obj instanceof PathAttachment) { - writePathAttachment(json, (PathAttachment) obj); + writePathAttachment((PathAttachment) obj); } else { throw new RuntimeException("Unknown VertexAttachment type: " + obj.getClass().getName()); } } - private void writeColor(JsonWriter json, Color color) { - if (color == null) { + private void writeColor(Color obj) { + if (obj == null) { json.writeNull(); } else { json.writeObjectStart(); json.writeName("r"); - json.writeValue(color.r); + json.writeValue(obj.r); json.writeName("g"); - json.writeValue(color.g); + json.writeValue(obj.g); json.writeName("b"); - json.writeValue(color.b); + json.writeValue(obj.b); json.writeName("a"); - json.writeValue(color.a); + json.writeValue(obj.a); json.writeObjectEnd(); } } - private void writeTextureRegion(JsonWriter json, TextureRegion region) { - if (region == null) { + private void writeTextureRegion(TextureRegion obj) { + if (obj == null) { json.writeNull(); } else { json.writeObjectStart(); json.writeName("u"); - json.writeValue(region.getU()); + json.writeValue(obj.getU()); json.writeName("v"); - json.writeValue(region.getV()); + json.writeValue(obj.getV()); json.writeName("u2"); - json.writeValue(region.getU2()); + json.writeValue(obj.getU2()); json.writeName("v2"); - json.writeValue(region.getV2()); + json.writeValue(obj.getV2()); json.writeName("width"); - json.writeValue(region.getRegionWidth()); + json.writeValue(obj.getRegionWidth()); json.writeName("height"); - json.writeValue(region.getRegionHeight()); + json.writeValue(obj.getRegionHeight()); json.writeObjectEnd(); } } + + private void writeIntArray(IntArray obj) { + if (obj == null) { + json.writeNull(); + } else { + json.writeArrayStart(); + for (int i = 0; i < obj.size; i++) { + json.writeValue(obj.get(i)); + } + json.writeArrayEnd(); + } + } + + private void writeFloatArray(FloatArray obj) { + if (obj == null) { + json.writeNull(); + } else { + json.writeArrayStart(); + for (int i = 0; i < obj.size; i++) { + json.writeValue(obj.get(i)); + } + json.writeArrayEnd(); + } + } } \ No newline at end of file diff --git a/tests/generate-java-serializer.ts b/tests/generate-java-serializer.ts index 3320bf38a..3efbef5c0 100644 --- a/tests/generate-java-serializer.ts +++ b/tests/generate-java-serializer.ts @@ -3,304 +3,110 @@ import * as fs from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'url'; -import type { ClassInfo, PropertyInfo } from './types'; +import type { SerializerIR, PublicMethod, WriteMethod, Property } from './generate-serializer-ir'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -function loadExclusions(): { types: Set, methods: Map>, fields: Map> } { - const exclusionsPath = path.resolve(__dirname, 'java-exclusions.txt'); - const types = new Set(); - const methods = new Map>(); - const fields = new Map>(); +function generatePropertyCode(property: Property, indent: string): string[] { + const lines: string[] = []; + const accessor = `obj.${property.getter}`; - if (!fs.existsSync(exclusionsPath)) { - return { types, methods, fields }; - } - - const content = fs.readFileSync(exclusionsPath, 'utf-8'); - const lines = content.split('\n'); - - for (const line of lines) { - const trimmed = line.trim(); - if (!trimmed || trimmed.startsWith('#')) continue; - - const parts = trimmed.split(/\s+/); - if (parts.length < 2) continue; - - const [type, className, property] = parts; - - switch (type) { - case 'type': - types.add(className); - break; - case 'method': - if (property) { - if (!methods.has(className)) { - methods.set(className, new Set()); - } - methods.get(className)!.add(property); - } - break; - case 'field': - if (property) { - if (!fields.has(className)) { - fields.set(className, new Set()); - } - fields.get(className)!.add(property); - } - break; - } - } - - return { types, methods, fields }; -} - -interface SerializedAnalysisResult { - classMap: [string, ClassInfo][]; - accessibleTypes: string[]; - abstractTypes: [string, string[]][]; - allTypesToGenerate: string[]; - typeProperties: [string, PropertyInfo[]][]; -} - -function generateWriteValue(output: string[], expression: string, type: string, indent: string, abstractTypes: Map, classMap: Map) { - // Handle null annotations - const isNullable = type.includes('@Null'); - type = type.replace(/@Null\s+/g, '').trim(); - - // Primitive types - if (['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long'].includes(type)) { - output.push(`${indent}json.writeValue(${expression});`); - return; - } - - // Check if it's an enum - need to handle both short and full names - let classInfo = classMap.get(type); - if (!classInfo && !type.includes('.')) { - // Try to find by short name - for (const [fullName, info] of classMap) { - if (fullName.split('.').pop() === type) { - classInfo = info; - break; - } - } - } - - if (classInfo?.isEnum) { - if (isNullable) { - output.push(`${indent}if (${expression} == null) {`); - output.push(`${indent} json.writeNull();`); - output.push(`${indent}} else {`); - output.push(`${indent} json.writeValue(${expression}.name());`); - output.push(`${indent}}`); - } else { - output.push(`${indent}json.writeValue(${expression}.name());`); - } - return; - } - - // Arrays - if (type.startsWith('Array<')) { - const innerType = type.match(/Array<(.+?)>/)![1].trim(); - if (isNullable) { - output.push(`${indent}if (${expression} == null) {`); - output.push(`${indent} json.writeNull();`); - output.push(`${indent}} else {`); - output.push(`${indent} json.writeArrayStart();`); - output.push(`${indent} for (${innerType} item : ${expression}) {`); - generateWriteValue(output, 'item', innerType, indent + ' ', abstractTypes, classMap); - output.push(`${indent} }`); - output.push(`${indent} json.writeArrayEnd();`); - output.push(`${indent}}`); - } else { - output.push(`${indent}json.writeArrayStart();`); - output.push(`${indent}for (${innerType} item : ${expression}) {`); - generateWriteValue(output, 'item', innerType, indent + ' ', abstractTypes, classMap); - output.push(`${indent}}`); - output.push(`${indent}json.writeArrayEnd();`); - } - return; - } - - if (type === 'IntArray' || type === 'FloatArray') { - if (isNullable) { - output.push(`${indent}if (${expression} == null) {`); - output.push(`${indent} json.writeNull();`); - output.push(`${indent}} else {`); - output.push(`${indent} json.writeArrayStart();`); - output.push(`${indent} for (int i = 0; i < ${expression}.size; i++) {`); - output.push(`${indent} json.writeValue(${expression}.get(i));`); - output.push(`${indent} }`); - output.push(`${indent} json.writeArrayEnd();`); - output.push(`${indent}}`); - } else { - output.push(`${indent}json.writeArrayStart();`); - output.push(`${indent}for (int i = 0; i < ${expression}.size; i++) {`); - output.push(`${indent} json.writeValue(${expression}.get(i));`); - output.push(`${indent}}`); - output.push(`${indent}json.writeArrayEnd();`); - } - return; - } - - if (type.endsWith('[]')) { - const elemType = type.slice(0, -2); - if (isNullable) { - output.push(`${indent}if (${expression} == null) {`); - output.push(`${indent} json.writeNull();`); - output.push(`${indent}} else {`); - output.push(`${indent} json.writeArrayStart();`); - // Handle nested arrays (like float[][]) - if (elemType.endsWith('[]')) { - const nestedType = elemType.slice(0, -2); - output.push(`${indent} for (${elemType} nestedArray : ${expression}) {`); - output.push(`${indent} if (nestedArray == null) {`); - output.push(`${indent} json.writeNull();`); - output.push(`${indent} } else {`); - output.push(`${indent} json.writeArrayStart();`); - output.push(`${indent} for (${nestedType} elem : nestedArray) {`); - output.push(`${indent} json.writeValue(elem);`); - output.push(`${indent} }`); - output.push(`${indent} json.writeArrayEnd();`); - output.push(`${indent} }`); - output.push(`${indent} }`); + switch (property.kind) { + case "primitive": + lines.push(`${indent}json.writeValue(${accessor});`); + break; + + case "object": + if (property.isNullable) { + lines.push(`${indent}if (${accessor} == null) {`); + lines.push(`${indent} json.writeNull();`); + lines.push(`${indent}} else {`); + lines.push(`${indent} ${property.writeMethodCall}(${accessor});`); + lines.push(`${indent}}`); } else { - output.push(`${indent} for (${elemType} item : ${expression}) {`); - generateWriteValue(output, 'item', elemType, indent + ' ', abstractTypes, classMap); - output.push(`${indent} }`); + lines.push(`${indent}${property.writeMethodCall}(${accessor});`); } - output.push(`${indent} json.writeArrayEnd();`); - output.push(`${indent}}`); - } else { - output.push(`${indent}json.writeArrayStart();`); - // Handle nested arrays (like float[][]) - if (elemType.endsWith('[]')) { - const nestedType = elemType.slice(0, -2); - output.push(`${indent}for (${elemType} nestedArray : ${expression}) {`); - output.push(`${indent} json.writeArrayStart();`); - output.push(`${indent} for (${nestedType} elem : nestedArray) {`); - output.push(`${indent} json.writeValue(elem);`); - output.push(`${indent} }`); - output.push(`${indent} json.writeArrayEnd();`); - output.push(`${indent}}`); + break; + + case "enum": + if (property.isNullable) { + lines.push(`${indent}if (${accessor} == null) {`); + lines.push(`${indent} json.writeNull();`); + lines.push(`${indent}} else {`); + lines.push(`${indent} json.writeValue(${accessor}.name());`); + lines.push(`${indent}}`); } else { - output.push(`${indent}for (${elemType} item : ${expression}) {`); - generateWriteValue(output, 'item', elemType, indent + ' ', abstractTypes, classMap); - output.push(`${indent}}`); + lines.push(`${indent}json.writeValue(${accessor}.name());`); } - output.push(`${indent}json.writeArrayEnd();`); - } - return; - } - - // Special cases for libGDX types - if (type === 'Color') { - output.push(`${indent}writeColor(${expression});`); - return; - } - - if (type === 'TextureRegion') { - output.push(`${indent}writeTextureRegion(${expression});`); - return; - } - - // Handle objects - const shortType = type.split('.').pop()!; - - // Check if this type exists in classMap (for abstract types that might not be in generated methods) - let foundInClassMap = classMap.has(type); - if (!foundInClassMap && !type.includes('.')) { - // Try to find by short name - for (const [fullName, info] of classMap) { - if (fullName.split('.').pop() === type) { - foundInClassMap = true; - // If it's abstract/interface, we need the instanceof chain - if (info.isAbstract || info.isInterface) { - type = fullName; // Use full name for abstract types + break; + + case "array": + if (property.isNullable) { + lines.push(`${indent}if (${accessor} == null) {`); + lines.push(`${indent} json.writeNull();`); + lines.push(`${indent}} else {`); + lines.push(`${indent} json.writeArrayStart();`); + lines.push(`${indent} for (${property.elementType} item : ${accessor}) {`); + if (property.elementKind === "primitive") { + lines.push(`${indent} json.writeValue(item);`); + } else { + lines.push(`${indent} ${property.writeMethodCall}(item);`); } - break; + lines.push(`${indent} }`); + lines.push(`${indent} json.writeArrayEnd();`); + lines.push(`${indent}}`); + } else { + lines.push(`${indent}json.writeArrayStart();`); + lines.push(`${indent}for (${property.elementType} item : ${accessor}) {`); + if (property.elementKind === "primitive") { + lines.push(`${indent} json.writeValue(item);`); + } else { + lines.push(`${indent} ${property.writeMethodCall}(item);`); + } + lines.push(`${indent}}`); + lines.push(`${indent}json.writeArrayEnd();`); } - } - } - - if (isNullable) { - output.push(`${indent}if (${expression} == null) {`); - output.push(`${indent} json.writeNull();`); - output.push(`${indent}} else {`); - output.push(`${indent} write${shortType}(${expression});`); - output.push(`${indent}}`); - } else { - output.push(`${indent}write${shortType}(${expression});`); + break; + + case "nestedArray": + if (property.isNullable) { + lines.push(`${indent}if (${accessor} == null) {`); + lines.push(`${indent} json.writeNull();`); + lines.push(`${indent}} else {`); + lines.push(`${indent} json.writeArrayStart();`); + lines.push(`${indent} for (${property.elementType}[] nestedArray : ${accessor}) {`); + lines.push(`${indent} if (nestedArray == null) {`); + lines.push(`${indent} json.writeNull();`); + lines.push(`${indent} } else {`); + lines.push(`${indent} json.writeArrayStart();`); + lines.push(`${indent} for (${property.elementType} elem : nestedArray) {`); + lines.push(`${indent} json.writeValue(elem);`); + lines.push(`${indent} }`); + lines.push(`${indent} json.writeArrayEnd();`); + lines.push(`${indent} }`); + lines.push(`${indent} }`); + lines.push(`${indent} json.writeArrayEnd();`); + lines.push(`${indent}}`); + } else { + lines.push(`${indent}json.writeArrayStart();`); + lines.push(`${indent}for (${property.elementType}[] nestedArray : ${accessor}) {`); + lines.push(`${indent} json.writeArrayStart();`); + lines.push(`${indent} for (${property.elementType} elem : nestedArray) {`); + lines.push(`${indent} json.writeValue(elem);`); + lines.push(`${indent} }`); + lines.push(`${indent} json.writeArrayEnd();`); + lines.push(`${indent}}`); + lines.push(`${indent}json.writeArrayEnd();`); + } + break; } + + return lines; } -function generateJavaSerializer(analysisData: SerializedAnalysisResult): string { +function generateJavaFromIR(ir: SerializerIR): string { const javaOutput: string[] = []; - // Convert arrays back to Maps - const classMap = new Map(analysisData.classMap); - const abstractTypes = new Map(analysisData.abstractTypes); - const typeProperties = new Map(analysisData.typeProperties); - - // Collect all types that need write methods - const typesNeedingMethods = new Set(); - - // Add all types from allTypesToGenerate - for (const type of analysisData.allTypesToGenerate) { - typesNeedingMethods.add(type); - } - - // Add all abstract types that are referenced (but not excluded) - const exclusions = loadExclusions(); - for (const [abstractType] of abstractTypes) { - if (!exclusions.types.has(abstractType)) { - typesNeedingMethods.add(abstractType); - } - } - - // Add types referenced in properties - for (const [typeName, props] of typeProperties) { - if (!typesNeedingMethods.has(typeName)) continue; - - for (const prop of props) { - let propType = prop.type.replace(/@Null\s+/g, '').trim(); - - // Extract type from Array - const arrayMatch = propType.match(/Array<(.+?)>/); - if (arrayMatch) { - propType = arrayMatch[1].trim(); - } - - // Extract type from Type[] - if (propType.endsWith('[]')) { - propType = propType.slice(0, -2); - } - - // Skip primitives and special types - if (['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long', - 'Color', 'TextureRegion', 'IntArray', 'FloatArray'].includes(propType)) { - continue; - } - - // Add the type if it's a class (but not excluded) - if (propType.match(/^[A-Z]/)) { - if (!exclusions.types.has(propType)) { - typesNeedingMethods.add(propType); - } - - // Also check if it's an abstract type in classMap - for (const [fullName, info] of classMap) { - if (fullName === propType || fullName.split('.').pop() === propType) { - if (info.isAbstract || info.isInterface && !exclusions.types.has(fullName)) { - typesNeedingMethods.add(fullName); - } - break; - } - } - } - } - } - // Generate Java file header javaOutput.push('package com.esotericsoftware.spine.utils;'); javaOutput.push(''); @@ -318,7 +124,6 @@ function generateJavaSerializer(analysisData: SerializedAnalysisResult): string javaOutput.push('import com.badlogic.gdx.utils.IntArray;'); javaOutput.push('import com.badlogic.gdx.utils.FloatArray;'); javaOutput.push(''); - javaOutput.push('import java.util.Locale;'); javaOutput.push('import java.util.Set;'); javaOutput.push('import java.util.HashSet;'); @@ -328,90 +133,49 @@ function generateJavaSerializer(analysisData: SerializedAnalysisResult): string javaOutput.push(' private JsonWriter json;'); javaOutput.push(''); - // Generate main entry methods - javaOutput.push(' public String serializeSkeletonData(SkeletonData data) {'); - javaOutput.push(' visitedObjects.clear();'); - javaOutput.push(' json = new JsonWriter();'); - javaOutput.push(' writeSkeletonData(data);'); - javaOutput.push(' json.close();'); - javaOutput.push(' return json.getString();'); - javaOutput.push(' }'); - javaOutput.push(''); - javaOutput.push(' public String serializeSkeleton(Skeleton skeleton) {'); - javaOutput.push(' visitedObjects.clear();'); - javaOutput.push(' json = new JsonWriter();'); - javaOutput.push(' writeSkeleton(skeleton);'); - javaOutput.push(' json.close();'); - javaOutput.push(' return json.getString();'); - javaOutput.push(' }'); - javaOutput.push(''); - javaOutput.push(' public String serializeAnimationState(AnimationState state) {'); - javaOutput.push(' visitedObjects.clear();'); - javaOutput.push(' json = new JsonWriter();'); - javaOutput.push(' writeAnimationState(state);'); - javaOutput.push(' json.close();'); - javaOutput.push(' return json.getString();'); - javaOutput.push(' }'); - javaOutput.push(''); + // Generate public methods + for (const method of ir.publicMethods) { + javaOutput.push(` public String ${method.name}(${method.paramType} ${method.paramName}) {`); + javaOutput.push(' visitedObjects.clear();'); + javaOutput.push(' json = new JsonWriter();'); + javaOutput.push(` ${method.writeMethodCall}(${method.paramName});`); + javaOutput.push(' json.close();'); + javaOutput.push(' return json.getString();'); + javaOutput.push(' }'); + javaOutput.push(''); + } - // Generate write methods for all types - const generatedMethods = new Set(); - - for (const typeName of Array.from(typesNeedingMethods).sort()) { - const classInfo = classMap.get(typeName); - if (!classInfo) continue; + // Generate write methods + for (const method of ir.writeMethods) { + const shortName = method.paramType.split('.').pop()!; + const className = method.paramType.includes('.') ? method.paramType : shortName; - // Skip enums - they are handled inline with .name() calls - if (classInfo.isEnum) continue; + javaOutput.push(` private void ${method.name}(${className} obj) {`); - const shortName = typeName.split('.').pop()!; - - // Skip if already generated (handle name collisions) - if (generatedMethods.has(shortName)) continue; - generatedMethods.add(shortName); - - // Use full class name for inner classes - const className = typeName.includes('.') ? typeName : shortName; - - javaOutput.push(` private void write${shortName}(${className} obj) {`); - - if (classInfo.isEnum) { - // Handle enums - javaOutput.push(' json.writeValue(obj.name());'); - } else if (classInfo.isAbstract || classInfo.isInterface) { + if (method.isAbstractType) { // Handle abstract types with instanceof chain - const implementations = classInfo.concreteImplementations || []; - - // Filter out excluded types from implementations - const exclusions = loadExclusions(); - const filteredImplementations = implementations.filter(impl => { - return !exclusions.types.has(impl); - }); - - if (filteredImplementations.length === 0) { - javaOutput.push(' json.writeNull(); // No concrete implementations after filtering exclusions'); - } else { + if (method.subtypeChecks && method.subtypeChecks.length > 0) { let first = true; - for (const impl of filteredImplementations) { - const implShortName = impl.split('.').pop()!; - const implClassName = impl.includes('.') ? impl : implShortName; + for (const subtype of method.subtypeChecks) { + const subtypeShortName = subtype.typeName.split('.').pop()!; + const subtypeClassName = subtype.typeName.includes('.') ? subtype.typeName : subtypeShortName; if (first) { - javaOutput.push(` if (obj instanceof ${implClassName}) {`); + javaOutput.push(` if (obj instanceof ${subtypeClassName}) {`); first = false; } else { - javaOutput.push(` } else if (obj instanceof ${implClassName}) {`); + javaOutput.push(` } else if (obj instanceof ${subtypeClassName}) {`); } - javaOutput.push(` write${implShortName}((${implClassName}) obj);`); + javaOutput.push(` ${subtype.writeMethodCall}((${subtypeClassName}) obj);`); } javaOutput.push(' } else {'); javaOutput.push(` throw new RuntimeException("Unknown ${shortName} type: " + obj.getClass().getName());`); javaOutput.push(' }'); + } else { + javaOutput.push(' json.writeNull(); // No concrete implementations after filtering exclusions'); } } else { // Handle concrete types - const properties = typeProperties.get(typeName) || []; - // Add cycle detection javaOutput.push(' if (visitedObjects.contains(obj)) {'); javaOutput.push(' json.writeValue("");'); @@ -426,22 +190,12 @@ function generateJavaSerializer(analysisData: SerializedAnalysisResult): string javaOutput.push(' json.writeName("type");'); javaOutput.push(` json.writeValue("${shortName}");`); - // Write properties (skip excluded ones) - for (const prop of properties) { - if (prop.excluded) { - javaOutput.push(` // Skipping excluded property: ${prop.name}`); - continue; - } - - const propName = prop.isGetter ? - prop.name.replace('get', '').replace('()', '').charAt(0).toLowerCase() + - prop.name.replace('get', '').replace('()', '').slice(1) : - prop.name; - + // Write properties + for (const property of method.properties) { javaOutput.push(''); - javaOutput.push(` json.writeName("${propName}");`); - const accessor = prop.isGetter ? `obj.${prop.name}` : `obj.${prop.name}`; - generateWriteValue(javaOutput, accessor, prop.type, ' ', abstractTypes, classMap); + javaOutput.push(` json.writeName("${property.name}");`); + const propertyLines = generatePropertyCode(property, ' '); + javaOutput.push(...propertyLines); } javaOutput.push(''); @@ -452,7 +206,7 @@ function generateJavaSerializer(analysisData: SerializedAnalysisResult): string javaOutput.push(''); } - // Add helper methods + // Add helper methods for special types javaOutput.push(' private void writeColor(Color obj) {'); javaOutput.push(' if (obj == null) {'); javaOutput.push(' json.writeNull();'); @@ -491,6 +245,34 @@ function generateJavaSerializer(analysisData: SerializedAnalysisResult): string javaOutput.push(' json.writeObjectEnd();'); javaOutput.push(' }'); javaOutput.push(' }'); + + // Add IntArray and FloatArray helper methods + javaOutput.push(''); + javaOutput.push(' private void writeIntArray(IntArray obj) {'); + javaOutput.push(' if (obj == null) {'); + javaOutput.push(' json.writeNull();'); + javaOutput.push(' } else {'); + javaOutput.push(' json.writeArrayStart();'); + javaOutput.push(' for (int i = 0; i < obj.size; i++) {'); + javaOutput.push(' json.writeValue(obj.get(i));'); + javaOutput.push(' }'); + javaOutput.push(' json.writeArrayEnd();'); + javaOutput.push(' }'); + javaOutput.push(' }'); + javaOutput.push(''); + + javaOutput.push(' private void writeFloatArray(FloatArray obj) {'); + javaOutput.push(' if (obj == null) {'); + javaOutput.push(' json.writeNull();'); + javaOutput.push(' } else {'); + javaOutput.push(' json.writeArrayStart();'); + javaOutput.push(' for (int i = 0; i < obj.size; i++) {'); + javaOutput.push(' json.writeValue(obj.get(i));'); + javaOutput.push(' }'); + javaOutput.push(' json.writeArrayEnd();'); + javaOutput.push(' }'); + javaOutput.push(' }'); + javaOutput.push('}'); return javaOutput.join('\n'); @@ -498,17 +280,17 @@ function generateJavaSerializer(analysisData: SerializedAnalysisResult): string async function main() { try { - // Read analysis result - const analysisFile = path.resolve(__dirname, '..', 'output', 'analysis-result.json'); - if (!fs.existsSync(analysisFile)) { - console.error('Analysis result not found. Run analyze-java-api.ts first.'); + // Read the IR file + const irFile = path.resolve(__dirname, 'output', 'serializer-ir.json'); + if (!fs.existsSync(irFile)) { + console.error('Serializer IR not found. Run generate-serializer-ir.ts first.'); process.exit(1); } - const analysisData: SerializedAnalysisResult = JSON.parse(fs.readFileSync(analysisFile, 'utf8')); + const ir: SerializerIR = JSON.parse(fs.readFileSync(irFile, 'utf8')); - // Generate Java serializer - const javaCode = generateJavaSerializer(analysisData); + // Generate Java serializer from IR + const javaCode = generateJavaFromIR(ir); // Write the Java file const javaFile = path.resolve( @@ -527,10 +309,13 @@ async function main() { fs.mkdirSync(path.dirname(javaFile), { recursive: true }); fs.writeFileSync(javaFile, javaCode); - console.log(`Generated Java serializer: ${javaFile}`); + console.log(`Generated Java serializer from IR: ${javaFile}`); + console.log(`- ${ir.publicMethods.length} public methods`); + console.log(`- ${ir.writeMethods.length} write methods`); } catch (error: any) { console.error('Error:', error.message); + console.error('Stack:', error.stack); process.exit(1); } } @@ -540,4 +325,4 @@ if (import.meta.url === `file://${process.argv[1]}`) { main(); } -export { generateJavaSerializer }; \ No newline at end of file +export { generateJavaFromIR }; \ No newline at end of file diff --git a/tests/generate-serializer-ir.ts b/tests/generate-serializer-ir.ts new file mode 100644 index 000000000..220a50da2 --- /dev/null +++ b/tests/generate-serializer-ir.ts @@ -0,0 +1,575 @@ +#!/usr/bin/env tsx + +import * as fs from 'fs'; +import * as path from 'path'; +import { fileURLToPath } from 'url'; +import type { ClassInfo, PropertyInfo } from './types'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +// IR Type Definitions +interface SerializerIR { + publicMethods: PublicMethod[]; + writeMethods: WriteMethod[]; + enumMappings: { [enumName: string]: { [javaValue: string]: string } }; +} + +interface PublicMethod { + name: string; + paramType: string; + paramName: string; + writeMethodCall: string; +} + +interface WriteMethod { + name: string; + paramType: string; + properties: Property[]; + isAbstractType: boolean; + subtypeChecks?: SubtypeCheck[]; +} + +interface SubtypeCheck { + typeName: string; + writeMethodCall: string; +} + +type Property = Primitive | Object | Enum | Array | NestedArray; + +interface Primitive { + kind: "primitive"; + name: string; + getter: string; + valueType: string; + isNullable: boolean; +} + +interface Object { + kind: "object"; + name: string; + getter: string; + valueType: string; + writeMethodCall: string; + isNullable: boolean; +} + +interface Enum { + kind: "enum"; + name: string; + getter: string; + enumName: string; + isNullable: boolean; +} + +interface Array { + kind: "array"; + name: string; + getter: string; + elementType: string; + elementKind: "primitive" | "object"; + writeMethodCall?: string; + isNullable: boolean; +} + +interface NestedArray { + kind: "nestedArray"; + name: string; + getter: string; + elementType: string; + isNullable: boolean; +} + +interface SerializedAnalysisResult { + classMap: [string, ClassInfo][]; + accessibleTypes: string[]; + abstractTypes: [string, string[]][]; + allTypesToGenerate: string[]; + typeProperties: [string, PropertyInfo[]][]; +} + +function loadExclusions(): { types: Set, methods: Map>, fields: Map> } { + const exclusionsPath = path.resolve(__dirname, 'java-exclusions.txt'); + const types = new Set(); + const methods = new Map>(); + const fields = new Map>(); + + if (!fs.existsSync(exclusionsPath)) { + return { types, methods, fields }; + } + + const content = fs.readFileSync(exclusionsPath, 'utf-8'); + const lines = content.split('\n'); + + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#')) continue; + + const parts = trimmed.split(/\s+/); + if (parts.length < 2) continue; + + const [type, className, property] = parts; + + switch (type) { + case 'type': + types.add(className); + break; + case 'method': + if (property) { + if (!methods.has(className)) { + methods.set(className, new Set()); + } + methods.get(className)!.add(property); + } + break; + case 'field': + if (property) { + if (!fields.has(className)) { + fields.set(className, new Set()); + } + fields.get(className)!.add(property); + } + break; + } + } + + return { types, methods, fields }; +} + +function analyzePropertyType(propType: string, classMap: Map): Property { + // Handle null annotations + const isNullable = propType.includes('@Null'); + propType = propType.replace(/@Null\s+/g, '').trim(); + + // Extract property name and getter from the type analysis + // This is a simplified version - in practice we'd get this from PropertyInfo + const name = "propertyName"; // placeholder + const getter = "getPropertyName"; // placeholder + + // Primitive types + if (['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long'].includes(propType)) { + return { + kind: "primitive", + name, + getter, + valueType: propType, + isNullable + }; + } + + // Check if it's an enum + let classInfo = classMap.get(propType); + if (!classInfo && !propType.includes('.')) { + // Try to find by short name + for (const [fullName, info] of classMap) { + if (fullName.split('.').pop() === propType) { + classInfo = info; + break; + } + } + } + + if (classInfo?.isEnum) { + return { + kind: "enum", + name, + getter, + enumName: propType.split('.').pop()!, + isNullable + }; + } + + // Arrays + if (propType.startsWith('Array<')) { + const innerType = propType.match(/Array<(.+?)>/)![1].trim(); + const elementKind = ['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long'].includes(innerType) ? "primitive" : "object"; + + return { + kind: "array", + name, + getter, + elementType: innerType, + elementKind, + writeMethodCall: elementKind === "object" ? `write${innerType}` : undefined, + isNullable + }; + } + + // Handle nested arrays (like float[][]) + if (propType.endsWith('[]')) { + const elemType = propType.slice(0, -2); + if (elemType.endsWith('[]')) { + const nestedType = elemType.slice(0, -2); + return { + kind: "nestedArray", + name, + getter, + elementType: nestedType, + isNullable + }; + } else { + const elementKind = ['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long'].includes(elemType) ? "primitive" : "object"; + return { + kind: "array", + name, + getter, + elementType: elemType, + elementKind, + writeMethodCall: elementKind === "object" ? `write${elemType}` : undefined, + isNullable + }; + } + } + + // Special libGDX types that get custom handling + if (['Color', 'TextureRegion', 'IntArray', 'FloatArray'].includes(propType)) { + return { + kind: "object", + name, + getter, + valueType: propType, + writeMethodCall: `write${propType}`, + isNullable + }; + } + + // Object types + const shortType = propType.split('.').pop()!; + return { + kind: "object", + name, + getter, + valueType: propType, + writeMethodCall: `write${shortType}`, + isNullable + }; +} + +function generateSerializerIR(analysisData: SerializedAnalysisResult): SerializerIR { + // Convert arrays back to Maps + const classMap = new Map(analysisData.classMap); + const abstractTypes = new Map(analysisData.abstractTypes); + const typeProperties = new Map(analysisData.typeProperties); + const exclusions = loadExclusions(); + + // Generate enum mappings + const enumMappings: { [enumName: string]: { [javaValue: string]: string } } = {}; + for (const [className, classInfo] of classMap) { + if (classInfo.isEnum && classInfo.enumValues) { + const shortName = className.split('.').pop()!; + const valueMap: { [javaValue: string]: string } = {}; + + for (const javaValue of classInfo.enumValues) { + // Convert Java enum value to C++ enum value + // e.g. "setup" -> "MixBlend_Setup", "first" -> "MixBlend_First" + const cppValue = `${shortName}_${javaValue.charAt(0).toUpperCase() + javaValue.slice(1)}`; + valueMap[javaValue] = cppValue; + } + + enumMappings[shortName] = valueMap; + } + } + + // Generate public methods + const publicMethods: PublicMethod[] = [ + { + name: "serializeSkeletonData", + paramType: "SkeletonData", + paramName: "data", + writeMethodCall: "writeSkeletonData" + }, + { + name: "serializeSkeleton", + paramType: "Skeleton", + paramName: "skeleton", + writeMethodCall: "writeSkeleton" + }, + { + name: "serializeAnimationState", + paramType: "AnimationState", + paramName: "state", + writeMethodCall: "writeAnimationState" + } + ]; + + // Collect all types that need write methods + const typesNeedingMethods = new Set(); + + // Add all types from allTypesToGenerate + for (const type of analysisData.allTypesToGenerate) { + typesNeedingMethods.add(type); + } + + // Add all abstract types that are referenced (but not excluded) + for (const [abstractType] of abstractTypes) { + if (!exclusions.types.has(abstractType)) { + typesNeedingMethods.add(abstractType); + } + } + + // Add types referenced in properties + for (const [typeName, props] of typeProperties) { + if (!typesNeedingMethods.has(typeName)) continue; + + for (const prop of props) { + let propType = prop.type.replace(/@Null\s+/g, '').trim(); + + // Extract type from Array + const arrayMatch = propType.match(/Array<(.+?)>/); + if (arrayMatch) { + propType = arrayMatch[1].trim(); + } + + // Extract type from Type[] + if (propType.endsWith('[]')) { + propType = propType.slice(0, -2); + } + + // Skip primitives and special types + if (['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long', + 'Color', 'TextureRegion', 'IntArray', 'FloatArray'].includes(propType)) { + continue; + } + + // Add the type if it's a class (but not excluded) + if (propType.match(/^[A-Z]/)) { + if (!exclusions.types.has(propType)) { + typesNeedingMethods.add(propType); + } + + // Also check if it's an abstract type in classMap + for (const [fullName, info] of classMap) { + if (fullName === propType || fullName.split('.').pop() === propType) { + if ((info.isAbstract || info.isInterface) && !exclusions.types.has(fullName)) { + typesNeedingMethods.add(fullName); + } + break; + } + } + } + } + } + + // Generate write methods + const writeMethods: WriteMethod[] = []; + const generatedMethods = new Set(); + + for (const typeName of Array.from(typesNeedingMethods).sort()) { + const classInfo = classMap.get(typeName); + if (!classInfo) continue; + + // Skip enums - they are handled inline with .name() calls + if (classInfo.isEnum) continue; + + const shortName = typeName.split('.').pop()!; + + // Skip if already generated (handle name collisions) + if (generatedMethods.has(shortName)) continue; + generatedMethods.add(shortName); + + const writeMethod: WriteMethod = { + name: `write${shortName}`, + paramType: typeName, + properties: [], + isAbstractType: classInfo.isAbstract || classInfo.isInterface || false + }; + + if (classInfo.isAbstract || classInfo.isInterface) { + // Handle abstract types with instanceof chain + const implementations = classInfo.concreteImplementations || []; + + // Filter out excluded types from implementations + const filteredImplementations = implementations.filter(impl => { + return !exclusions.types.has(impl); + }); + + if (filteredImplementations.length > 0) { + writeMethod.subtypeChecks = filteredImplementations.map(impl => { + const implShortName = impl.split('.').pop()!; + return { + typeName: impl, + writeMethodCall: `write${implShortName}` + }; + }); + } + } else { + // Handle concrete types - convert properties + const properties = typeProperties.get(typeName) || []; + + for (const prop of properties) { + if (prop.excluded) { + continue; // Skip excluded properties + } + + const propName = prop.isGetter ? + prop.name.replace('get', '').replace('()', '').charAt(0).toLowerCase() + + prop.name.replace('get', '').replace('()', '').slice(1) : + prop.name; + + // Handle getter vs field access + let getter: string; + if (prop.isGetter) { + // It's a method call - ensure it has parentheses + getter = prop.name.includes('()') ? prop.name : `${prop.name}()`; + } else { + // It's a field access - use the field name directly + getter = prop.name; + } + + // Analyze the property type to determine the correct Property variant + const irProperty = analyzePropertyWithDetails(prop, propName, getter, classMap); + writeMethod.properties.push(irProperty); + } + } + + writeMethods.push(writeMethod); + } + + return { + publicMethods, + writeMethods, + enumMappings + }; +} + +function analyzePropertyWithDetails(prop: PropertyInfo, propName: string, getter: string, classMap: Map): Property { + // Handle null annotations + const isNullable = prop.type.includes('@Null'); + let propType = prop.type.replace(/@Null\s+/g, '').trim(); + + // Primitive types + if (['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long'].includes(propType)) { + return { + kind: "primitive", + name: propName, + getter, + valueType: propType, + isNullable + }; + } + + // Check if it's an enum + let classInfo = classMap.get(propType); + if (!classInfo && !propType.includes('.')) { + // Try to find by short name + for (const [fullName, info] of classMap) { + if (fullName.split('.').pop() === propType) { + classInfo = info; + propType = fullName; // Use full name + break; + } + } + } + + if (classInfo?.isEnum) { + return { + kind: "enum", + name: propName, + getter, + enumName: propType.split('.').pop()!, + isNullable + }; + } + + // Arrays + if (propType.startsWith('Array<')) { + const innerType = propType.match(/Array<(.+?)>/)![1].trim(); + const elementKind = ['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long'].includes(innerType) ? "primitive" : "object"; + + return { + kind: "array", + name: propName, + getter, + elementType: innerType, + elementKind, + writeMethodCall: elementKind === "object" ? `write${innerType.split('.').pop()}` : undefined, + isNullable + }; + } + + // Handle nested arrays (like float[][]) + if (propType.endsWith('[]')) { + const elemType = propType.slice(0, -2); + if (elemType.endsWith('[]')) { + const nestedType = elemType.slice(0, -2); + return { + kind: "nestedArray", + name: propName, + getter, + elementType: nestedType, + isNullable + }; + } else { + const elementKind = ['String', 'int', 'float', 'boolean', 'short', 'byte', 'double', 'long'].includes(elemType) ? "primitive" : "object"; + return { + kind: "array", + name: propName, + getter, + elementType: elemType, + elementKind, + writeMethodCall: elementKind === "object" ? `write${elemType.split('.').pop()}` : undefined, + isNullable + }; + } + } + + // Special libGDX types that get custom handling + if (['Color', 'TextureRegion', 'IntArray', 'FloatArray'].includes(propType)) { + return { + kind: "object", + name: propName, + getter, + valueType: propType, + writeMethodCall: `write${propType}`, + isNullable + }; + } + + // Object types + const shortType = propType.split('.').pop()!; + return { + kind: "object", + name: propName, + getter, + valueType: propType, + writeMethodCall: `write${shortType}`, + isNullable + }; +} + +async function main() { + try { + // Read analysis result + const analysisFile = path.resolve(__dirname, '..', 'output', 'analysis-result.json'); + if (!fs.existsSync(analysisFile)) { + console.error('Analysis result not found. Run analyze-java-api.ts first.'); + process.exit(1); + } + + const analysisData: SerializedAnalysisResult = JSON.parse(fs.readFileSync(analysisFile, 'utf8')); + + // Generate IR + const ir = generateSerializerIR(analysisData); + + // Write the IR file + const irFile = path.resolve(__dirname, 'output', 'serializer-ir.json'); + fs.mkdirSync(path.dirname(irFile), { recursive: true }); + fs.writeFileSync(irFile, JSON.stringify(ir, null, 2)); + + console.log(`Generated serializer IR: ${irFile}`); + console.log(`- ${ir.publicMethods.length} public methods`); + console.log(`- ${ir.writeMethods.length} write methods`); + console.log(`- ${Object.keys(ir.enumMappings).length} enum mappings`); + + } catch (error: any) { + console.error('Error:', error.message); + console.error('Stack:', error.stack); + process.exit(1); + } +} + +// Allow running as a script or importing the function +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +} + +export { generateSerializerIR, type SerializerIR, type PublicMethod, type WriteMethod, type Property }; \ No newline at end of file diff --git a/tests/regenerate-all.sh b/tests/regenerate-all.sh index 12ca98b9e..4e3816462 100755 --- a/tests/regenerate-all.sh +++ b/tests/regenerate-all.sh @@ -8,6 +8,9 @@ cd "$SCRIPT_DIR/.." echo "Analyzing Java API..." npx tsx tests/analyze-java-api.ts +echo "Generating serializer IR..." +npx tsx tests/generate-serializer-ir.ts + echo "Generating Java SkeletonSerializer..." npx tsx tests/generate-java-serializer.ts