import { PropertyFactory , ArrayProperty , BaseProperty , ReferenceProperty , ReferenceArrayProperty , ReferenceMapProperty , ValueProperty , ContainerProperty , EnumProperty , EnumArrayProperty , MapProperty , SetProperty , } from "@fluid-experimental/property-properties" ; import { ComponentMap } from "./componentMap" ; import { PropertyProxy } from "./propertyProxy" ; import { PropertyProxyErrors } from "./errors" ; import { NonPrimitiveTypes , ReferenceType } from "./interfaces" ; export function forceType < T > ( value ) { return true ; } hidden export namespace Utilities { param property param updateFunction export function wrapWithPushPopNotificationDelayScope ( property , updateFunction ) { if ( property . getWorkspace ( ) ) { property . getWorkspace ( ) . pushNotificationDelayScope ( ) ; updateFunction ( ) ; property . getWorkspace ( ) . popNotificationDelayScope ( ) ; } else { updateFunction ( ) ; } } /**
     * Prepares an element for insertion. If `element` is a (proxied)  external * and `property` is of a primitive type the returned element will be a javascript primitive.
     * If `property` is of a non-primitive type the returned element will be `property`. This is only
     * different if `property` is an  external (returns a clone of `element`
     * if `caller` is 'copyWithin' or 'fill' and `element` removed from `property` so that
     * it no longer has a parent for 'reverse' and 'sort).
     * If `element` is not a  external the returned element will be `element`
     * if `property` is not an  external * or a  external .
     * In that case the returned element will be `element` only if `property` is of a primitive type.
     *  param property param element param caller Only used if the property parameter is an  external .
     * Triggers special behavior for the methods copyWithin(), fill(), reverse(), sort().
     *  return export function prepareElementForInsertion ( property , element , caller ? ) { if ( element && typeof element . getProperty === "function" && PropertyFactory . instanceOf ( element . getProperty ( ) , "BaseProperty" ) ) { element = element . getProperty ( ) ; } if ( PropertyFactory . instanceOf ( element , "BaseProperty" ) ) { if ( property . isPrimitiveType ( ) && ! PropertyFactory . instanceOf ( property , "Reference" , "array" ) && ! PropertyFactory . instanceOf ( property , "Reference" , "map" ) ) { if ( element . isPrimitiveType ( ) ) { return element . getValue ( ) ; } else { return element . getValues ( ) ; } } else { if ( element . getParent ( ) && property . getContext ( ) === "array" && forceType < ArrayProperty > ( property ) ) { if ( caller === "copyWithin" || caller === "fill" ) { return element . clone ( ) ; } else if ( caller === "reverse" || caller === "sort" || caller === "swap" ) { const idxString = element . getRelativePath ( element . getParent ( ) ) ; const idx = parseInt ( idxString . substr ( 1 ) . slice ( 0 , - 1 ) , 10 ) ; const removed = property . remove ( idx ) ; property . insert ( idx , PropertyFactory . create ( property . getTypeid ( ) , "single" ) ) ; return removed ; } else { return element ; } } else { return element ; } } } else { if ( property . getContext ( ) !== "single" && element && typeof element !== "string" && element [ Symbol . iterator ] && typeof element [ Symbol . iterator ] === "function" ) { throw new Error ( PropertyProxyErrors . ITERABLE_INSERTION ) ; } if ( property . getContext ( ) === "array" || property . getContext ( ) === "map" ) { if ( property . isPrimitiveType ( ) || property . getFullTypeid ( ) . includes ( "array<enum<" ) ) { return element ; } else { return PropertyFactory . create ( property . getTypeid ( ) , "single" , element ) ; } } else { return element ; } } } param property param value export function assign ( property , value ) { const context = property . getContext ( ) ; if ( value && value . getProperty ) { value = value . getProperty ( ) ; } if ( context === "single" ) { if ( PropertyFactory . instanceOf ( value , "BaseProperty" ) ) { if ( PropertyFactory . instanceOf ( property , "Reference" ) && forceType < ReferenceProperty > ( property ) ) { property . set ( value ) ; } else { property . deserialize ( value . serialize ( ) ) ; } } else { throwOnIterableForSingleProperty ( value ) ; if ( property . isPrimitiveType ( ) && forceType < ValueProperty > ( property ) ) { property . setValue ( value ) ; } else { if ( forceType < ContainerProperty > ( property ) ) { property . setValues ( value ) ; } } } } else { let valueContext ; if ( PropertyFactory . instanceOf ( value , "BaseProperty" ) ) { valueContext = value . getContext ( ) ; } wrapWithPushPopNotificationDelayScope ( property , ( ) => { if ( context === "array" && forceType < ArrayProperty > ( property ) ) { const proxiedArray = PropertyProxy . proxify ( property ) ; property . clear ( ) ; if ( valueContext === "array" ) { if ( value . isPrimitiveType ( ) ) { proxiedArray . getProperty ( ) . setValues ( value . getValues ( ) ) ; } else { PropertyProxy . proxify ( value as ArrayProperty ) . forEach ( ( el ) => { proxiedArray . push ( el . getProperty ( ) . clone ( ) ) ; } ) ; } } else { const elements = _getElementsArray ( value ) ; elements . forEach ( ( el ) => proxiedArray . push ( el ) ) ; } } else if ( context === "map" && forceType < MapProperty > ( property ) ) { const proxiedMap = PropertyProxy . proxify ( property ) ; proxiedMap . clear ( ) ; if ( valueContext === "map" ) { if ( value . isPrimitiveType ( ) ) { proxiedMap . getProperty ( ) . setValues ( value . getValues ( ) ) ; } else { PropertyProxy . proxify ( value as MapProperty ) . forEach ( ( el , key ) => { proxiedMap . set ( key , el . getProperty ( ) . clone ( ) ) ; } ) ; } } else { const elements = _getElementsArray ( value ) ; elements . forEach ( ( el ) => proxiedMap . set ( el [ 0 ] , el [ 1 ] ) ) ; } } else { const proxiedSet = PropertyProxy . proxify ( property as MapProperty ) ; proxiedSet . clear ( ) ; if ( valueContext === "set" && forceType < SetProperty > ( value ) ) { PropertyProxy . proxify ( value ) . forEach ( ( el ) => { proxiedSet . add ( el . getProperty ( ) . clone ( ) ) ; } ) ; } else { const elements = _getElementsArray ( value ) ; elements . forEach ( ( el ) => proxiedSet . add ( el ) ) ; } } } ) ; } } param value export function throwOnIterableForSingleProperty ( value ) { if ( value && typeof value !== "string" && value [ Symbol . iterator ] && typeof value [ Symbol . iterator ] === "function" ) { throw new Error ( PropertyProxyErrors . ASSIGN_ITERABLE_TO_SINGLE ) ; } } param property param key param value export function setValueOfReferencedProperty ( property , key , value ) { const keys = ( key === undefined ? [ ] : [ key ] ) ; if ( ! ( property . isReferenceValid as any ) ( ... keys ) ) { throw new Error ( PropertyProxyErrors . INVALID_REFERENCE ) ; } const { referencedPropertyParent , relativePathFromParent } = PropertyProxy . getParentOfReferencedProperty ( property , ... keys ) ; const proxiedReferencedPropertyParent = PropertyProxy . proxify ( referencedPropertyParent as NonPrimitiveTypes ) ; if ( proxiedReferencedPropertyParent instanceof ComponentMap ) { proxiedReferencedPropertyParent . set ( relativePathFromParent , value ) ; } else { proxiedReferencedPropertyParent [ relativePathFromParent ] = value ; } } param key return export const containsAsterisk = ( key ) => ( String ( key ) === key && key [ key . length - 1 ] === "*" ) ; param key return export const containsCaret = ( key ) => ( String ( key ) === key && key [ key . length - 1 ] === "^" ) ; param property param key param caretFound param isReferenceCollection return { Object | Proxy } export function proxifyInternal ( property , key , caretFound , isReferenceCollection = false ) { const context = property . getContext ( ) ; const propertyAtKey = ( property . get as any ) ( key ) ! ; if ( PropertyFactory . instanceOf ( propertyAtKey , "BaseProperty" ) ) { if ( caretFound && propertyAtKey . isPrimitiveType ( ) ) { if ( PropertyFactory . instanceOf ( propertyAtKey , "Enum" ) && forceType < EnumProperty > ( propertyAtKey ) ) { return propertyAtKey . getEnumString ( ) ; } else if ( PropertyFactory . instanceOf ( propertyAtKey , "Uint64" ) || PropertyFactory . instanceOf ( propertyAtKey , "Int64" ) ) { return propertyAtKey . toString ( ) ; } } return PropertyProxy . proxify ( propertyAtKey ) ; } else { if ( caretFound ) { const contextIsSingle = context === "single" ; let other_property = property ; if ( ! contextIsSingle && isReferenceCollection ) { const data = PropertyProxy . getParentOfReferencedProperty ( property as ReferenceMapProperty | ReferenceArrayProperty , key ) ; other_property = data . referencedPropertyParent ; key = data . relativePathFromParent ; } if ( contextIsSingle && forceType < string > ( key ) ) { const data = PropertyProxy . getParentOfReferencedProperty ( ( property . get as any ) ( key , { referenceResolutionMode : BaseProperty . REFERENCE_RESOLUTION . NO_LEAFS } ) ! ) ; other_property = data . referencedPropertyParent ; key = data . relativePathFromParent ; } const typeid = other_property . getTypeid ( ) ; const fullTypeid = other_property . getFullTypeid ( ) ; if ( typeid === "Uint64" ) { return PropertyFactory . create ( "Uint64" , "single" , propertyAtKey ) . toString ( ) ; } else if ( typeid === "Int64" ) { return PropertyFactory . create ( "Int64" , "single" , propertyAtKey ) . toString ( ) ; } else if ( fullTypeid . includes ( "<enum<" ) && forceType < EnumArrayProperty > ( other_property ) && forceType < number > ( key ) ) { return other_property . getEnumString ( key ) ; } } return propertyAtKey ; } } } param value return hidden function _getElementsArray < T = any > ( value ? ) { if ( ! value || typeof value [ Symbol . iterator ] !== "function" || String ( value ) === value ) { throw new Error ( PropertyProxyErrors . NON_ITERABLE ) ; } return [ ... value ] ; }