Skip to content

Commit

Permalink
move serialization to prototype, see phetsims/joist#970
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanolson committed Jun 12, 2024
1 parent d32f61c commit a4aaf75
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 22 deletions.
4 changes: 2 additions & 2 deletions js/NumberProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ export default class NumberProperty extends Property<number> implements TRangedP
/**
* Get parent state and append NumberProperty-specific metadata to it.
*/
public toStateObject(): NumberPropertyState {
const parentStateObject = PropertyIOImpl.toStateObject( this );
public override toStateObject<StateType>(): NumberPropertySelfState & ReadOnlyPropertyState<StateType> {
const parentStateObject = super.toStateObject<StateType>() as ( NumberPropertySelfState & ReadOnlyPropertyState<StateType> );

parentStateObject.numberType = this.numberType;
parentStateObject.range = Range.RangeIO.toStateObject( this.rangeProperty.value );
Expand Down
58 changes: 38 additions & 20 deletions js/ReadOnlyProperty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ export default class ReadOnlyProperty<T> extends PhetioObject implements TReadOn
protected readonly valueValidator: Validator<T>;
public static readonly TANDEM_NAME_SUFFIX: string = 'Property';

// The IOType for the values this Property supports.
protected readonly phetioValueType: IOType;

/**
* This is protected to indicate to clients that subclasses should be used instead.
* @param value - the initial value of the property
Expand Down Expand Up @@ -180,6 +183,7 @@ export default class ReadOnlyProperty<T> extends PhetioObject implements TReadOn
this.isDeferred = false;
this.deferredValue = null;
this.hasDeferredValue = false;
this.phetioValueType = options.phetioValueType;

this.valueValidator = _.pick( options, Validation.VALIDATOR_KEYS );
this.valueValidator.validationMessage = this.valueValidator.validationMessage || 'Property value not valid';
Expand Down Expand Up @@ -326,6 +330,38 @@ export default class ReadOnlyProperty<T> extends PhetioObject implements TReadOn
this._notifyListeners( null );
}

/**
* Implementation of serialization for PhET-iO support.
*
* This function is parameterized to support subtyping. That said, it is a bit useless, since we don't want to
* parameterize ReadOnlyProperty in general to the IOType's state type, so please bear with us.
*/
protected toStateObject<StateType>(): ReadOnlyPropertyState<StateType> {
assert && assert( this.phetioValueType.toStateObject, `toStateObject doesn't exist for ${this.phetioValueType.typeName}` );
const stateObject: ReadOnlyPropertyState<StateType> = {
value: this.phetioValueType.toStateObject( this.value ),

// Only include validValues if specified, so they only show up in PhET-iO Studio when supplied.
validValues: this.validValues ? this.validValues.map( v => {
return this.phetioValueType.toStateObject( v );
} ) : null,
units: NullableIO( StringIO ).toStateObject( this.units )
};

return stateObject;
}

protected applyState<StateType>( stateObject: ReadOnlyPropertyState<StateType> ): void {
const units = NullableIO( StringIO ).fromStateObject( stateObject.units );
assert && assert( this.units === units, 'Property units do not match' );
assert && assert( this.isSettable(), 'Property should be settable' );
this.unguardedSet( this.phetioValueType.fromStateObject( stateObject.value ) );

if ( stateObject.validValues ) {
this.validValues = stateObject.validValues.map( ( validValue: StateType ) => ( this.phetioValueType ).fromStateObject( validValue ) );
}
}

/**
* When deferred, set values do not take effect or send out notifications. After defer ends, the Property takes
* its deferred value (if any), and a follow-up action (return value) can be invoked to send out notifications
Expand Down Expand Up @@ -533,28 +569,10 @@ export default class ReadOnlyProperty<T> extends PhetioObject implements TReadOn
events: [ ReadOnlyProperty.CHANGED_EVENT_NAME ],
parameterTypes: [ parameterType ],
toStateObject: property => {
assert && assert( parameterType.toStateObject, `toStateObject doesn't exist for ${parameterType.typeName}` );
const stateObject: ReadOnlyPropertyState<StateType> = {
value: parameterType.toStateObject( property.value ),

// Only include validValues if specified, so they only show up in PhET-iO Studio when supplied.
validValues: property.validValues ? property.validValues.map( v => {
return parameterType.toStateObject( v );
} ) : null,
units: NullableIO( StringIO ).toStateObject( property.units )
};

return stateObject;
return property.toStateObject();
},
applyState: ( property, stateObject ) => {
const units = NullableIO( StringIO ).fromStateObject( stateObject.units );
assert && assert( property.units === units, 'Property units do not match' );
assert && assert( property.isSettable(), 'Property should be settable' );
property.unguardedSet( parameterType.fromStateObject( stateObject.value ) );

if ( stateObject.validValues ) {
property.validValues = stateObject.validValues.map( ( validValue: StateType ) => parameterType.fromStateObject( validValue ) );
}
property.applyState( stateObject );
},
stateSchema: {
value: parameterType,
Expand Down

0 comments on commit a4aaf75

Please sign in to comment.