13 KiB
Haxe Serializer: Java Getter to Haxe Field/Method Mapping Analysis
Overview
This document contains a comprehensive analysis of ALL patterns for transforming Java getter calls into appropriate Haxe field/method access, extracted from the java-haxe-diff.md file.
General Patterns
1. Simple Field Access Pattern (Most Common)
Java getter getX() maps to Haxe field x:Type
Examples:
getName()→name:StringgetDuration()→duration:FloatgetTimeScale()→timeScale:FloatgetLoop()→loop:BoolgetX()→x:FloatgetY()→y:FloatgetRotation()→rotation:FloatgetScaleX()→scaleX:FloatgetScaleY()→scaleY:FloatgetShearX()→shearX:FloatgetShearY()→shearY:FloatgetWidth()→width:FloatgetHeight()→height:FloatgetColor()→color:ColorgetAlpha()→alpha:FloatgetMix()→mix:FloatgetVisible()→visible:BoolgetIndex()→index:Int
2. Array Field Access Pattern
Java getter getX() returns array → Haxe field x:Array<Type>
Examples:
getTimelines()→timelines:Array<Timeline>getBones()→bones:Array<Int>orbones:Array<BoneData>orbones:Array<BonePose>(context-dependent)getChildren()→children:Array<Bone>getFrames()→frames:Array<Float>getAttachmentNames()→attachmentNames:Array<String>getVertices()→vertices:Array<Float>orvertices:Array<Array<Float>>(context-dependent)getEvents()→events:Array<Event>getDrawOrders()→drawOrders:Array<Array<Int>>getSlots()→slots:Array<Slot>orslots:Array<SlotData>(context-dependent)getTracks()→tracks:Array<TrackEntry>getTriangles()→triangles:Array<Int>getUVs()→uvs:Array<Float>getRegionUVs()→regionUVs:Array<Float>getEdges()→edges:Array<Int>getLengths()→lengths:Array<Float>getRegions()→regions:Array<TextureRegion>getAnimations()→animations:Array<Animation>getSkins()→skins:Array<Skin>getConstraints()→constraints:Array<Constraint<Dynamic, Dynamic, Dynamic>>orconstraints:Array<ConstraintData<Dynamic, Dynamic>>getPhysicsConstraints()→physics:Array<PhysicsConstraint>getProperties()→properties:Array<FromProperty>getDeform()→deform:Array<Float>
3. Method Remains Method Pattern
Some getters remain as methods in Haxe, typically those that perform calculations or have side effects.
Examples:
getFrameCount()→getFrameCount():IntgetFrameEntries()→getFrameEntries():IntgetDuration()→getDuration():Float(in Timeline classes)getSlotIndex()→getSlotIndex():IntgetBoneIndex()→getBoneIndex():IntgetConstraintIndex()→getConstraintIndex():IntgetData()→getData():T(where T is the data type)getPose()→getPose():T(where T is the pose type)getAppliedPose()→getAppliedPose():TgetSetupPose()→getSetupPose():TgetAnimationTime()→getAnimationTime():FloatgetTrackComplete()→getTrackComplete():FloatgetWorldRotationX()→getWorldRotationX():FloatgetWorldRotationY()→getWorldRotationY():FloatgetWorldScaleX()→getWorldScaleX():FloatgetWorldScaleY()→getWorldScaleY():FloatgetAttachments()→getAttachments():Array<SkinEntry>getOffsetRotation()→getOffsetRotation():FloatgetOffsetX()→getOffsetX():FloatgetOffsetY()→getOffsetY():FloatgetOffsetScaleX()→getOffsetScaleX():FloatgetOffsetScaleY()→getOffsetScaleY():FloatgetOffsetShearY()→getOffsetShearY():Float
4. Special Property Access Pattern
Some properties use Haxe's property syntax with get/set accessors.
Examples:
getName()→name(get, never):String(in Attachment subclasses)getRootBone()→rootBone(get, never):BonegetScaleY()→scaleY(get, default):Float(in Skeleton)
5. Field Name Variations
Some getters have slight variations in their Haxe field names.
Examples:
getInt()→intValue:IntgetFloat()→floatValue:FloatgetString()→stringValue:StringgetUpdateCache()→_updateCache:Array<Dynamic>(with underscore prefix)getPropertyIds()→propertyIds:Array<String>getDefaultSkin()→defaultSkin:Skin
6. Type Reference Patterns
Getters that reference other objects.
Examples:
getParent()→parent:Boneorparent:BoneData(context-dependent)getTarget()→target:Boneortarget:BoneData(context-dependent)getSource()→source:Boneorsource:BoneData(context-dependent)getAttachment()→attachment:Attachmentorattachment:VertexAttachment(context-dependent)getSlot()→slot:Slotorslot:SlotData(context-dependent)getBone()→bone:Boneorbone:BoneDataorbone:BonePose(context-dependent)getSkin()→skin:SkingetAnimation()→animation:AnimationgetRegion()→region:TextureRegiongetSequence()→sequence:SequencegetParentMesh()→parentMesh:MeshAttachmentgetEndSlot()→endSlot:SlotData
Context-Dependent Mappings
1. getBones() mapping depends on containing class:
- In
Animation: →bones:Array<Int> - In
BoneData,IkConstraintData,PathConstraintData,TransformConstraintData,Skin: →bones:Array<BoneData> - In
IkConstraint,PathConstraint,TransformConstraint: →bones:Array<BonePose> - In
BoundingBoxAttachment,ClippingAttachment,MeshAttachment,PathAttachment,VertexAttachment: →bones:Array<Int>
2. getVertices() mapping depends on containing class:
- In
DeformTimeline: →vertices:Array<Array<Float>>(2D array) - In
ClippingAttachment,MeshAttachment,PathAttachment,VertexAttachment: →vertices:Array<Float>(1D array)
3. getDuration() mapping depends on containing class:
- In
Animation: →duration:Float(field) - In Timeline classes: →
getDuration():Float(method)
4. Special Cases in Timeline Classes:
All Timeline subclasses have these getters as methods:
getFrameCount()→getFrameCount():IntgetFrameEntries()→getFrameEntries():IntgetDuration()→getDuration():FloatgetPropertyIds()→propertyIds:Array<String>(field)getFrames()→frames:Array<Float>(field)
5. Special Cases in Constraint Classes:
getData()→getData():T(method returning specific data type)getPose()→getPose():T(method returning specific pose type)getAppliedPose()→getAppliedPose():T(method returning specific pose type)
Type Transformations
Java to Haxe Type Mappings:
int→Intfloat→Floatdouble→Floatboolean→BoolString→StringArray/List→Array<T>IntArray→Array<Int>(custom type)- Object types remain the same (e.g.,
Color→Color)
Special Edge Cases
1. Incomplete Mappings (marked as TODO in the file):
BonePose.getInherit()→ TODOBoundingBoxAttachment.getVertices()→ TODOBoundingBoxAttachment.getWorldVerticesLength()→ TODOBoundingBoxAttachment.getTimelineAttachment()→ TODOBoundingBoxAttachment.getId()→ TODOBoundingBoxAttachment.getName()→ TODO
2. Inherited Methods:
Some getters are inherited from parent classes and noted as such:
getName()in attachment classes inherits fromAttachment- Properties in
FromRotate,FromScaleX, etc. inherit fromFromProperty - Properties in constraint data classes inherit from
PosedData
3. Special Skeleton Fields:
getUpdateCache()→_updateCache:Array<Dynamic>(private with underscore)getRootBone()→rootBone(get, never):Bone(computed property)getScaleY()→scaleY(get, default):Float(property with default)
4. DrawOrderTimeline Exception:
getFrameCount()→frameCount:Int(field instead of method, unlike other timelines)
5. Enum and Constant Mappings:
getBlendMode()→blendMode:BlendModegetPositionMode()→positionMode:PositionModegetSpacingMode()→spacingMode:SpacingModegetRotateMode()→rotateMode:RotateModegetMixBlend()→mixBlend:MixBlendgetInherit()→inherit:Inherit
Summary
The transformation rules can be categorized as:
- Default Rule:
getX()→x:Type(lowercase first letter, remove get prefix) - Method Preservation: Keep as method for calculated values or methods with side effects
- Special Properties: Use Haxe property syntax for computed/readonly properties
- Context Awareness: Same getter can map differently based on containing class
- Type Transformation: Java primitive types map to Haxe equivalents
- Special Cases: Some fields have custom names (e.g.,
getInt()→intValue)
When implementing the Haxe serializer generator, these patterns should be applied in order of specificity:
- Check for exact class + getter combinations first
- Check for class-specific patterns (e.g., all Timeline getters)
- Apply general transformation rules
- Handle special cases and exceptions
Haxe Serializer Generator Implementation Plan
Based on the comprehensive pattern analysis above, here's the implementation plan for a new Haxe serializer generator:
Architecture Overview
The new generator will use a rule-based transformation system with the following components:
- Mapping Database: Load java-haxe-diff.md mappings into a structured lookup table
- Context-Aware Transformer: Apply transformations based on class context
- Type System: Handle Java-to-Haxe type conversions
- Code Generator: Produce clean, idiomatic Haxe code
Implementation Steps
Phase 1: Build Mapping Infrastructure
-
Parse java-haxe-diff.md
- Extract all type mappings into a structured format
- Create lookup table:
Map<ClassName, Map<JavaGetter, HaxeMapping>> - Store mapping type (field, method, property) and Haxe type info
-
Create Transformation Rules Engine
- Rule priority system (specific → general)
- Context-aware lookups (class + getter combination)
- Fallback to general patterns
Phase 2: Implement Core Transformations
-
Getter-to-Field Transformer
- Check mapping database first
- Apply general pattern:
getX()→x - Handle special cases (getInt → intValue, etc.)
-
Type Transformer
- Java primitives → Haxe types
- Array handling (including nested arrays)
- Generic type resolution
-
Access Pattern Resolver
- Determine if result is field access or method call
- Handle property syntax
name(get, never) - Preserve method calls where needed
Phase 3: Code Generation
-
Property Code Generator
- Generate correct Haxe syntax based on mapping type
- Handle nullable types properly
- Generate enum switch statements with correct Haxe enum syntax
-
Method Generator
- Handle abstract types with
Std.isOfType - Generate proper casting syntax
- Implement special methods (writeSkin, writeSkinEntry)
- Handle abstract types with
Phase 4: Validation and Testing
-
Compile-time Validation
- Generate code and attempt Haxe compilation
- Report type errors with context
-
Runtime Testing
- Compare serialization output with Java reference
- Ensure all fields are properly serialized
Key Design Decisions
- Data-Driven Approach: Use the mapping file as the source of truth rather than hardcoded rules
- Explicit Over Implicit: When in doubt, use the exact mapping from java-haxe-diff.md
- Fail-Fast: If a mapping is missing or ambiguous, fail with a clear error message
- Type Safety: Leverage Haxe's type system to catch errors at compile time
Implementation Details
Mapping Database Structure
interface HaxeMapping {
kind: 'field' | 'method' | 'property';
haxeName: string;
haxeType: string;
propertyGetter?: string; // for (get, never) syntax
}
interface ClassMappings {
className: string;
getters: Map<string, HaxeMapping>;
}
Transformation Algorithm
1. Load all mappings from java-haxe-diff.md
2. For each property in IR:
a. Look up exact class + getter combination
b. If not found, check for class-level patterns
c. If not found, apply general transformation rules
d. Transform type from Java to Haxe
e. Generate appropriate access code
Special Handling
- Timeline Classes: All timeline getters follow consistent patterns
- Constraint Classes: Handle getData/getPose/getAppliedPose consistently
- Array Properties: Detect 1D vs 2D arrays based on context
- Enum Values: Generate proper Haxe enum access syntax
- Circular References: Maintain visitedObjects tracking
Error Handling
- Missing Mappings: Log unmapped getters with class context
- Type Mismatches: Detect and report Java/Haxe type incompatibilities
- Compilation Errors: Capture and display Haxe compiler output
Testing Strategy
- Unit Tests: Test individual transformation rules
- Integration Tests: Generate full serializer and compile
- Snapshot Tests: Compare output with reference implementation
This approach ensures accuracy, maintainability, and extensibility while leveraging the comprehensive mapping data we've collected.