<commit_before>// Copyright 2018-2021, University of Colorado Boulder

/**
 * IO Type for phet-core Enumeration that supports serializing and deserializing values. Cannot be moved to the core
 * type since Enumeration must be defined before ValidatorDef can be defined.
 *
 * @author <NAME> (PhET Interactive Simulations)
 */

import Property from '../../../../axon/js/Property.js';
import IOType from '../../../../tandem/js/types/IOType.js';
import StateSchema from '../../../../tandem/js/types/StateSchema.js';

// {Map.<enumeration:Enumeration, IOType>} - Cache each parameterized RichEnumuerationIO so that it is only created once.
const cache = new Map();

/**
 * This caching implementation should be kept in sync with the other parametric IO Type caching implementations.
 */
const RichEnumuerationIO = <T>( enumeration: T, keys: string[], values: T[] ): IOType => {

  if ( !cache.has( enumeration ) ) {
    const getKey = ( value: T ) => keys.find( key =>

      // @ts-ignore
      enumeration[ key ] === value );

    // Enumeration supports additional documentation, so the values can be described.
    const additionalDocs = 'docs';//enumeration.phetioDocumentation ? ` ${enumeration.phetioDocumentation}` : '';

    cache.set( enumeration, new IOType( `RichEnumuerationIO(${keys.join( '|' )})`, {
      validValues: values,
      documentation: `Possible values: ${keys.join( ', ' )}.${additionalDocs}`,
      toStateObject: getKey,
      fromStateObject: ( stateObject: string ) => {
        assert && assert( typeof stateObject === 'string', 'unsupported RichEnumuerationIO value type, expected string' );
        assert && assert( keys.indexOf( stateObject ) >= 0, `Unrecognized value: ${stateObject}` );

        // @ts-ignore
        return enumeration[ stateObject ];
      },
      stateSchema: StateSchema.asValue( `${keys.join( '|' )}`, {
        isValidValue: ( v: string ) => keys.includes( v )
      } )
    } ) );
  }

  return cache.get( enumeration );
};

class RichEnumerationProperty<T> extends Property<T> {
  constructor( EnumerationType: any, value: T, providedOptions?: any ) {
    providedOptions = providedOptions || {};
    const validValues = [];
    const keys = [];
    for ( const property in EnumerationType ) {
      if ( EnumerationType[ property ] instanceof EnumerationType ) {
        validValues.push( EnumerationType[ property ] );
        keys.push( property );
      }
    }
    providedOptions.phetioType = Property.PropertyIO( RichEnumuerationIO<T>( EnumerationType, keys, validValues ) );
    super( value, providedOptions );
  }
}

export default RichEnumerationProperty;<commit_msg>Add phet-io instrumentation and update enumeration IO type, https://github.com/phetsims/chipper/issues/1106<commit_after>// Copyright 2018-2021, University of Colorado Boulder

/**
 * IO Type for phet-core Enumeration that supports serializing and deserializing values. Cannot be moved to the core
 * type since Enumeration must be defined before ValidatorDef can be defined.
 *
 * @author <NAME> (PhET Interactive Simulations)
 */

import Property from '../../../../axon/js/Property.js';
import IOType from '../../../../tandem/js/types/IOType.js';
import StateSchema from '../../../../tandem/js/types/StateSchema.js';

// {Map.<enumeration:Enumeration, IOType>} - Cache each parameterized RichEnumuerationIO so that it is only created once.
const cache = new Map();

/**
 * This caching implementation should be kept in sync with the other parametric IO Type caching implementations.
 */
const RichEnumuerationIO = <T>( enumeration: any ): IOType => {

  if ( !cache.has( enumeration ) ) {

    const keyToValueMap = new Map<string, T>();
    const valueToKeyMap = new Map<T, string>();

    for ( const key in enumeration ) {
      const value = enumeration[ key ];
      if ( value instanceof enumeration ) {
        keyToValueMap.set( key, value );
        valueToKeyMap.set( value, key );
      }
    }

    const keys = Array.from( keyToValueMap.keys() );
    const values = Array.from( keyToValueMap.values() );

    // Enumeration supports additional documentation, so the values can be described.
    const additionalDocs = 'docs';//enumeration.phetioDocumentation ? ` ${enumeration.phetioDocumentation}` : '';

    cache.set( enumeration, new IOType( `RichEnumuerationIO(${keys.join( '|' )})`, {
      validValues: values,
      documentation: `Possible values: ${keys.join( ', ' )}.${additionalDocs}`,
      toStateObject: ( t: T ) => valueToKeyMap.get( t ),
      fromStateObject: ( stateObject: string ) => {
        assert && assert( typeof stateObject === 'string', 'unsupported RichEnumuerationIO value type, expected string' );
        assert && assert( keys.indexOf( stateObject ) >= 0, `Unrecognized value: ${stateObject}` );
        return keyToValueMap.get( stateObject )!;
      },
      stateSchema: StateSchema.asValue( `${keys.join( '|' )}`, {
        isValidValue: ( v: string ) => keys.includes( v )
      } )
    } ) );
  }

  return cache.get( enumeration );
};

class RichEnumerationProperty<T> extends Property<T> {
  constructor( EnumerationType: any, value: T, providedOptions?: any ) {
    providedOptions = providedOptions || {};
    providedOptions.validValues = _.values( EnumerationType ).filter( value => value instanceof EnumerationType );
    providedOptions.phetioType = Property.PropertyIO( RichEnumuerationIO<T>( EnumerationType ) );
    super( value, providedOptions );
  }
}

export default RichEnumerationProperty;
{'fraction_non_alphanumeric': 0.13563636363636364, 'fraction_numerical': 0.004, 'mean_word_length': 3.4687246141348496, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 29, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3005730', 'n_tokens_mistral': 1602, 'n_tokens_neox': 1531, 'n_words': 455}