@fluid-experimental/property-properties 0.54.2 → 0.56.0-49831

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dist/properties/arrayProperty.d.ts.map +1 -1
  2. package/dist/properties/arrayProperty.js +4 -4
  3. package/dist/properties/arrayProperty.js.map +1 -1
  4. package/dist/properties/baseProperty.d.ts +1 -1
  5. package/dist/properties/baseProperty.d.ts.map +1 -1
  6. package/dist/properties/baseProperty.js +13 -12
  7. package/dist/properties/baseProperty.js.map +1 -1
  8. package/dist/properties/boolProperty.d.ts.map +1 -1
  9. package/dist/properties/boolProperty.js +3 -2
  10. package/dist/properties/boolProperty.js.map +1 -1
  11. package/dist/properties/containerProperty.d.ts +1 -1
  12. package/dist/properties/containerProperty.d.ts.map +1 -1
  13. package/dist/properties/containerProperty.js +12 -3
  14. package/dist/properties/containerProperty.js.map +1 -1
  15. package/dist/properties/enumArrayProperty.js +2 -1
  16. package/dist/properties/enumArrayProperty.js.map +1 -1
  17. package/dist/properties/enumProperty.d.ts.map +1 -1
  18. package/dist/properties/enumProperty.js +2 -1
  19. package/dist/properties/enumProperty.js.map +1 -1
  20. package/dist/properties/floatProperties.d.ts.map +1 -1
  21. package/dist/properties/floatProperties.js +6 -4
  22. package/dist/properties/floatProperties.js.map +1 -1
  23. package/dist/properties/indexedCollectionBaseProperty.d.ts +2 -2
  24. package/dist/properties/indexedCollectionBaseProperty.d.ts.map +1 -1
  25. package/dist/properties/indexedCollectionBaseProperty.js +2 -2
  26. package/dist/properties/indexedCollectionBaseProperty.js.map +1 -1
  27. package/dist/properties/intProperties.d.ts.map +1 -1
  28. package/dist/properties/intProperties.js +15 -10
  29. package/dist/properties/intProperties.js.map +1 -1
  30. package/dist/properties/mapProperty.d.ts.map +1 -1
  31. package/dist/properties/mapProperty.js +2 -1
  32. package/dist/properties/mapProperty.js.map +1 -1
  33. package/dist/properties/namedNodeProperty.js +2 -1
  34. package/dist/properties/namedNodeProperty.js.map +1 -1
  35. package/dist/properties/namedProperty.js +2 -1
  36. package/dist/properties/namedProperty.js.map +1 -1
  37. package/dist/properties/nodeProperty.d.ts.map +1 -1
  38. package/dist/properties/nodeProperty.js +3 -1
  39. package/dist/properties/nodeProperty.js.map +1 -1
  40. package/dist/properties/referenceArrayProperty.js +2 -1
  41. package/dist/properties/referenceArrayProperty.js.map +1 -1
  42. package/dist/properties/referenceMapProperty.js +2 -1
  43. package/dist/properties/referenceMapProperty.js.map +1 -1
  44. package/dist/properties/referenceProperty.d.ts +1 -1
  45. package/dist/properties/referenceProperty.d.ts.map +1 -1
  46. package/dist/properties/referenceProperty.js +3 -2
  47. package/dist/properties/referenceProperty.js.map +1 -1
  48. package/dist/properties/setProperty.d.ts.map +1 -1
  49. package/dist/properties/setProperty.js +3 -2
  50. package/dist/properties/setProperty.js.map +1 -1
  51. package/dist/properties/stringProperty.d.ts.map +1 -1
  52. package/dist/properties/stringProperty.js +12 -3
  53. package/dist/properties/stringProperty.js.map +1 -1
  54. package/dist/properties/uintProperties.d.ts.map +1 -1
  55. package/dist/properties/uintProperties.js +9 -6
  56. package/dist/properties/uintProperties.js.map +1 -1
  57. package/dist/properties/valueArrayProperty.d.ts +1 -1
  58. package/dist/properties/valueArrayProperty.d.ts.map +1 -1
  59. package/dist/properties/valueArrayProperty.js +25 -13
  60. package/dist/properties/valueArrayProperty.js.map +1 -1
  61. package/dist/properties/valueMapProperty.d.ts.map +1 -1
  62. package/dist/properties/valueMapProperty.js +38 -26
  63. package/dist/properties/valueMapProperty.js.map +1 -1
  64. package/dist/propertyFactory.d.ts.map +1 -1
  65. package/dist/propertyFactory.js +442 -86
  66. package/dist/propertyFactory.js.map +1 -1
  67. package/dist/propertyTemplateWrapper.d.ts +0 -34
  68. package/dist/propertyTemplateWrapper.d.ts.map +1 -1
  69. package/dist/propertyTemplateWrapper.js +0 -108
  70. package/dist/propertyTemplateWrapper.js.map +1 -1
  71. package/dist/test/propertyFactory.spec.js +5 -2
  72. package/dist/test/propertyFactory.spec.js.map +1 -1
  73. package/dist/test/tsconfig.tsbuildinfo +11 -11
  74. package/lib/properties/arrayProperty.js +4 -4
  75. package/lib/properties/arrayProperty.js.map +1 -1
  76. package/lib/properties/baseProperty.js +13 -12
  77. package/lib/properties/baseProperty.js.map +1 -1
  78. package/lib/properties/boolProperty.js +3 -2
  79. package/lib/properties/boolProperty.js.map +1 -1
  80. package/lib/properties/containerProperty.js +12 -3
  81. package/lib/properties/containerProperty.js.map +1 -1
  82. package/lib/properties/enumArrayProperty.js +2 -1
  83. package/lib/properties/enumArrayProperty.js.map +1 -1
  84. package/lib/properties/enumProperty.js +2 -1
  85. package/lib/properties/enumProperty.js.map +1 -1
  86. package/lib/properties/floatProperties.js +6 -4
  87. package/lib/properties/floatProperties.js.map +1 -1
  88. package/lib/properties/indexedCollectionBaseProperty.js +2 -2
  89. package/lib/properties/indexedCollectionBaseProperty.js.map +1 -1
  90. package/lib/properties/intProperties.js +15 -10
  91. package/lib/properties/intProperties.js.map +1 -1
  92. package/lib/properties/mapProperty.js +2 -1
  93. package/lib/properties/mapProperty.js.map +1 -1
  94. package/lib/properties/namedNodeProperty.js +2 -1
  95. package/lib/properties/namedNodeProperty.js.map +1 -1
  96. package/lib/properties/namedProperty.js +2 -1
  97. package/lib/properties/namedProperty.js.map +1 -1
  98. package/lib/properties/nodeProperty.js +3 -1
  99. package/lib/properties/nodeProperty.js.map +1 -1
  100. package/lib/properties/referenceArrayProperty.js +2 -1
  101. package/lib/properties/referenceArrayProperty.js.map +1 -1
  102. package/lib/properties/referenceMapProperty.js +2 -1
  103. package/lib/properties/referenceMapProperty.js.map +1 -1
  104. package/lib/properties/referenceProperty.js +3 -2
  105. package/lib/properties/referenceProperty.js.map +1 -1
  106. package/lib/properties/setProperty.js +3 -2
  107. package/lib/properties/setProperty.js.map +1 -1
  108. package/lib/properties/stringProperty.js +12 -3
  109. package/lib/properties/stringProperty.js.map +1 -1
  110. package/lib/properties/uintProperties.js +9 -6
  111. package/lib/properties/uintProperties.js.map +1 -1
  112. package/lib/properties/valueArrayProperty.js +25 -13
  113. package/lib/properties/valueArrayProperty.js.map +1 -1
  114. package/lib/properties/valueMapProperty.js +38 -26
  115. package/lib/properties/valueMapProperty.js.map +1 -1
  116. package/lib/propertyFactory.js +442 -86
  117. package/lib/propertyFactory.js.map +1 -1
  118. package/lib/propertyTemplateWrapper.js +0 -108
  119. package/lib/propertyTemplateWrapper.js.map +1 -1
  120. package/package.json +5 -5
@@ -15,7 +15,7 @@ const fastestJSONCopy = require('fastest-json-copy');
15
15
  const deepCopy = fastestJSONCopy.copy;
16
16
  const { Collection, ConsoleUtils, EventEmitter, SortedCollection, constants, GuidUtils } = require('@fluid-experimental/property-common');
17
17
  const { MSG } = constants;
18
- const { TypeIdHelper, TemplateValidator, PathHelper } = require('@fluid-experimental/property-changeset');
18
+ const { TypeIdHelper, TemplateValidator, PathHelper, ChangeSet } = require('@fluid-experimental/property-changeset');
19
19
  const { PropertyTemplate } = require('./propertyTemplate');
20
20
  const { PropertyTemplateWrapper } = require('./propertyTemplateWrapper');
21
21
  // Include the property classes
@@ -286,6 +286,15 @@ class PropertyFactory {
286
286
  this._inheritanceCache = {};
287
287
  /** Cache of constructor function that are auto-generated for typeids */
288
288
  this._typedPropertyConstructorCache = {};
289
+ /** A cache of functions that create the properties */
290
+ this._cachedCreationFunctions = new Map();
291
+ /** Usually we will use the precompiled creation functions, but those all share the same constant properties.
292
+ * Since it is allowed to overwrite constants via default values, we have to explicitly instantiate new
293
+ * property instances for constants. Since the constants themselves may contain nested property instances,
294
+ * we use this flag to indicate that for all nested properties, we do not want to use the precompiled
295
+ * instantiation functions.
296
+ */
297
+ this._forceInstantion = false;
289
298
  this._init();
290
299
  }
291
300
  ;
@@ -796,13 +805,10 @@ class PropertyFactory {
796
805
  * Accepted values are "single" (default), "array", "map" and "set".
797
806
  * @param {object|undefined} in_initialProperties A set of initial values for the PropertySet being created
798
807
  * @param {string|undefined} in_scope - The scope in which the property typeid is defined
799
- * @param {boolean|undefined} in_optimizeConstants - set true if constant optimization should occur
800
- * @throws if the property does not have a unique id.
801
- * @throws if the property has a typeid that is not registered.
802
808
  * @return {property-properties.BaseProperty|undefined} the property instance
803
809
  * @private
804
810
  */
805
- _createProperty(in_typeid, in_context, in_initialProperties, in_scope, in_optimizeConstants) {
811
+ _createProperty(in_typeid, in_context, in_initialProperties, in_scope) {
806
812
  const ifNotSingleOrUndefined = (in_context || 'single') !== 'single';
807
813
  ConsoleUtils.assert(ifNotSingleOrUndefined || _.isString(in_typeid), MSG.UNKNOWN_TYPEID_SPECIFIED + in_typeid);
808
814
  let context = in_context;
@@ -814,33 +820,366 @@ class PropertyFactory {
814
820
  context = splitTypeId.context;
815
821
  }
816
822
  }
817
- let property;
818
- if (in_optimizeConstants) {
819
- var templateOrProperty = this._getWrapper(in_typeid, undefined, in_scope);
820
- var isProperty = templateOrProperty instanceof PropertyTemplateWrapper;
821
- var evaluateConstants = isProperty ? !templateOrProperty.hasConstantTree() : false;
822
- property = this._createFromPropertyDeclaration({
823
+ let propertyCreationFunction = undefined;
824
+ if (!this._forceInstantion) {
825
+ // Check, whether we already have a property creation function for this property
826
+ // in the cache
827
+ const scopeFunctionEntry = this._cachedCreationFunctions.get(in_typeid);
828
+ const contextFunctionEntry = scopeFunctionEntry && scopeFunctionEntry.get(in_scope);
829
+ propertyCreationFunction = contextFunctionEntry && contextFunctionEntry.get(context);
830
+ }
831
+ // If we don't have a cached function or are requested to explicitly instantiate the property
832
+ // we have to first create a property definition by recursively traversing all templates
833
+ let propertyDef;
834
+ if (!propertyCreationFunction) {
835
+ propertyDef = {};
836
+ this._createDefFromPropertyDeclaration({
823
837
  typeid: in_typeid,
824
838
  context: context || 'single'
825
- }, undefined, in_scope, evaluateConstants);
826
- if (isProperty) {
827
- templateOrProperty.loadConstants(property);
839
+ }, in_scope, propertyDef);
840
+ }
841
+ let property;
842
+ if (!this._forceInstantion) {
843
+ // If we don't yet have a creation function, we will create one here
844
+ propertyCreationFunction = propertyCreationFunction ||
845
+ this._definePropertyCreationFunction(propertyDef, in_typeid, in_scope, context);
846
+ // Create the property by invoking the precompiled creation function
847
+ property = propertyCreationFunction();
848
+ // If initial properties have been provided, we will assign them to the
849
+ // default initialized property
850
+ if (in_initialProperties !== undefined) {
851
+ this._setInitialValue(property, {
852
+ value: in_initialProperties
853
+ }, false);
828
854
  }
829
855
  }
830
856
  else {
831
- property = this._createFromPropertyDeclaration({
832
- typeid: in_typeid,
833
- context: context || 'single'
834
- }, undefined, in_scope, true);
835
- }
836
- if (in_initialProperties !== undefined) {
837
- this._setInitialValue(property, {
857
+ // Directly instantiate the property from the definition (without using) a precompield function
858
+ property = this._instantiatePropertyDef(propertyDef, in_scope, in_initialProperties && {
838
859
  value: in_initialProperties
839
860
  });
840
861
  }
841
862
  return property;
842
863
  }
843
864
  ;
865
+ /**
866
+ * Creates an instance of the property described in the property definition.
867
+ *
868
+ * Note: this function won't create any constant children, it is only used to
869
+ * instantiate nested constant properties and those will be set to constant
870
+ * after their instantiation.
871
+ *
872
+ * @param {Object} propertyDef - The property defintion for the property to create
873
+ * @param {String} in_scope - The scope for the property to create
874
+ * @param {String} in_initialProperties - The initial values for the property
875
+ *
876
+ * @returns {BaseProperty} An instance of the property
877
+ */
878
+ _instantiatePropertyDef(propertyDef, in_scope, in_initialProperties) {
879
+ let rootProperty = undefined;
880
+ // This stack is used to recursively iterate over the property definition
881
+ const creationStack = [{
882
+ id: undefined,
883
+ entry: propertyDef,
884
+ parent: undefined
885
+ }];
886
+ while (creationStack.length > 0) {
887
+ const currentEntry = creationStack.pop();
888
+ // We have an entry on the stack that is just waiting for its children to finish, but has already
889
+ // been created
890
+ if (currentEntry.signalChildrenFinished) {
891
+ currentEntry.property._signalAllStaticMembersHaveBeenAdded(in_scope);
892
+ if (currentEntry.initialValue) {
893
+ this._setInitialValue(currentEntry.property, currentEntry.initialValue, true);
894
+ }
895
+ continue;
896
+ }
897
+ // Create the property instance
898
+ let property = new (currentEntry.entry.constructorFunction)(currentEntry.entry.entry);
899
+ // Insert / append the property to the parent
900
+ if (currentEntry.parent) {
901
+ if (currentEntry.entry.optional) {
902
+ currentEntry.parent._insert(property.getId(), property, true);
903
+ }
904
+ else {
905
+ currentEntry.parent._append(property, currentEntry.entry.allowChildMerges);
906
+ }
907
+ }
908
+ else {
909
+ // If we are at the root, we store the property object to return it later
910
+ rootProperty = property;
911
+ }
912
+ // For named properties we have to assign a GUID (note: all constant properties in
913
+ // a template will share this GUID)
914
+ if (currentEntry.setGuid) {
915
+ property.value = GuidUtils.generateGUID();
916
+ }
917
+ // Assign optional children
918
+ if (currentEntry.entry.optionalChildren) {
919
+ for (let [id, typeid] of Object.entries(currentEntry.entry.optionalChildren)) {
920
+ property._addOptionalChild(id, typeid);
921
+ }
922
+ }
923
+ // Recursively process all children of this entry
924
+ if (currentEntry.entry.children) {
925
+ // Create an entry on the stack, which is later needed,
926
+ // to signal that all child properties have been added
927
+ const parentStackEntry = {
928
+ signalChildrenFinished: true,
929
+ initialValue: currentEntry.entry.initialValue,
930
+ property
931
+ };
932
+ creationStack.push(parentStackEntry);
933
+ for (let [id, child] of currentEntry.entry.children) {
934
+ creationStack.push({
935
+ parent: property,
936
+ id: id,
937
+ entry: child,
938
+ setGuid: currentEntry.entry.assignGuid && id ===
939
+ 'guid'
940
+ });
941
+ }
942
+ }
943
+ else {
944
+ // If there are no children, we directly assign the initial value and
945
+ // signal that the property has completely been initialized
946
+ if (currentEntry.entry.initialValue) {
947
+ this._setInitialValue(property, currentEntry.entry.initialValue, true);
948
+ }
949
+ if (currentEntry.entry.signal) {
950
+ property._signalAllStaticMembersHaveBeenAdded(in_scope);
951
+ }
952
+ }
953
+ }
954
+ if (in_initialProperties !== undefined) {
955
+ this._setInitialValue(rootProperty, in_initialProperties, true);
956
+ }
957
+ return rootProperty;
958
+ }
959
+ /**
960
+ * Creates a javascript function that instantiates the requested property
961
+ *
962
+ * @param {Object} propertyDef - The property defintion for the property for which the function is created
963
+ * @param {String} in_typeid - The typeid for the property for which the function is created
964
+ * @param {String} in_scope - The scope for the property for which the function is created
965
+ * @param {String} in_context - The context for the property for which the function is created
966
+ *
967
+ * @returns {Function} A function that creates an instance of the property
968
+ */
969
+ _definePropertyCreationFunction(propertyDef, in_typeid, in_scope, in_context) {
970
+ // This stack is used to recursively iterate over the property definition
971
+ const creationStack = [{
972
+ id: null,
973
+ def: propertyDef,
974
+ parent: undefined
975
+ }];
976
+ let creationFunctionSource = "";
977
+ let currentParameterIndex = 0;
978
+ let parameters = [];
979
+ let currentPropertyNumber = 0;
980
+ let currentPropertyVarName = "";
981
+ let resultVarName;
982
+ while (creationStack.length > 0) {
983
+ const currentEntry = creationStack.pop();
984
+ // We have an entry on the stack that is just waiting for its children to finish, but has already
985
+ // been created
986
+ if (currentEntry.signalChildrenFinished) {
987
+ // Add the signalling function
988
+ creationFunctionSource +=
989
+ `${currentEntry.propertyVarname}._signalAllStaticMembersHaveBeenAdded(${JSON.stringify(in_scope)});\n`;
990
+ continue;
991
+ }
992
+ // Determine the initial value for this property
993
+ let initialValue = currentEntry.def.initialValue !== undefined ?
994
+ currentEntry.def.initialValue :
995
+ undefined;
996
+ if (currentEntry.def.entry.id) {
997
+ let parentEntry = currentEntry.parentStackEntry;
998
+ let path = [currentEntry.def.entry.id];
999
+ // We have to walk the whole parent chain and extract for
1000
+ // each parent the initial values. Entries further up in the
1001
+ // chain can overwrite entries further down
1002
+ while (parentEntry) {
1003
+ if (parentEntry.initialValue) {
1004
+ // Extract changes to be applied to this property
1005
+ let filteredChangeSet = parentEntry.initialValue.value;
1006
+ for (let i = 0; i < path.length; i++) {
1007
+ filteredChangeSet = filteredChangeSet && filteredChangeSet[path[i]];
1008
+ }
1009
+ // Update the initial value with the extract changeset
1010
+ if (_.isObject(filteredChangeSet)) {
1011
+ if (initialValue === undefined) {
1012
+ initialValue = {
1013
+ typed: false,
1014
+ value: filteredChangeSet
1015
+ };
1016
+ }
1017
+ else if (_.isObject(initialValue)) {
1018
+ Object.assign(initialValue.value, filteredChangeSet);
1019
+ }
1020
+ else {
1021
+ throw new Error('Invalid default values specified');
1022
+ }
1023
+ }
1024
+ else if (filteredChangeSet !== undefined) {
1025
+ if (initialValue === undefined) {
1026
+ initialValue = {
1027
+ value: undefined,
1028
+ typed: false
1029
+ };
1030
+ }
1031
+ initialValue.value = filteredChangeSet;
1032
+ }
1033
+ }
1034
+ if (parentEntry.id !== null) {
1035
+ path.unshift(parentEntry.id);
1036
+ }
1037
+ parentEntry = parentEntry.parentStackEntry;
1038
+ }
1039
+ }
1040
+ if (currentEntry.def.constant) {
1041
+ // If we have a constant property, we create a concrete property object instance and share it
1042
+ // among all instances of the parent property
1043
+ let instantiatedChild;
1044
+ try {
1045
+ // Usually we would use the precompiled creation functions, but those all share
1046
+ // the same constant properties. Since it is allowed to overwrite constants via
1047
+ // default values, we have to explicitly instantiate new property instances for
1048
+ // constants. Since the constants themselves may contain nested property instances,
1049
+ // we use this flag to indicate that for all nested properties, we do not want to use
1050
+ // the precompiled instantiation functions.
1051
+ this._forceInstantion = true;
1052
+ instantiatedChild =
1053
+ this._instantiatePropertyDef(currentEntry.def, in_scope, currentEntry.def.initialValue);
1054
+ }
1055
+ finally {
1056
+ this._forceInstantion = false;
1057
+ }
1058
+ instantiatedChild._setAsConstant();
1059
+ // Add a reference to the newly instantiate constant property to the parameters and add
1060
+ // a command to add it into the tree
1061
+ parameters.push(instantiatedChild);
1062
+ creationFunctionSource += `${currentEntry.parentVarName}._append(
1063
+ parameters[${currentParameterIndex}], ${currentEntry.def.allowChildMerges}
1064
+ );\n`;
1065
+ currentParameterIndex += 1;
1066
+ }
1067
+ else {
1068
+ // Put the constructor function and the description of the property into the
1069
+ // parameters array
1070
+ parameters.push(currentEntry.def.constructorFunction);
1071
+ parameters.push(currentEntry.def.entry);
1072
+ // and add the instantiation call to the generated function
1073
+ currentPropertyNumber++;
1074
+ currentPropertyVarName = `property${currentPropertyNumber}`;
1075
+ creationFunctionSource +=
1076
+ `const ${currentPropertyVarName} =
1077
+ new parameters[${currentParameterIndex}](parameters[${currentParameterIndex + 1}]);\n`;
1078
+ currentParameterIndex += 2;
1079
+ // Insert / append the property to the parent
1080
+ if (currentEntry.parentVarName !== undefined) {
1081
+ if (currentEntry.def.optional) {
1082
+ creationFunctionSource += `${currentEntry.parentVarName}._insert(
1083
+ ${JSON.stringify(currentEntry.def.entry.id)}, ${currentPropertyVarName}, true
1084
+ );\n`;
1085
+ }
1086
+ else {
1087
+ creationFunctionSource += `${currentEntry.parentVarName}._append(
1088
+ ${currentPropertyVarName}, ${currentEntry.def.allowChildMerges}
1089
+ );\n`;
1090
+ }
1091
+ }
1092
+ else {
1093
+ resultVarName = currentPropertyVarName;
1094
+ }
1095
+ // For named properties we have to add a calll to assign a GUID to the function
1096
+ if (currentEntry.setGuid) {
1097
+ creationFunctionSource += `${currentPropertyVarName}.value = GuidUtils.generateGUID();\n`;
1098
+ }
1099
+ // And if there are any optional children, we add them here (should this be further optimized? I
1100
+ // propbably would not have to be done on every instantiation, those could be stored in the prototype)
1101
+ if (currentEntry.def.optionalChildren) {
1102
+ for (let [id, typeid] of Object.entries(currentEntry.def.optionalChildren)) {
1103
+ creationFunctionSource += `${currentPropertyVarName}._addOptionalChild(
1104
+ ${JSON.stringify(id)},
1105
+ ${JSON.stringify(typeid)}
1106
+ );\n`;
1107
+ }
1108
+ }
1109
+ // Recursively process all children of this entry
1110
+ if (currentEntry.def.children) {
1111
+ // Create an entry on the stack, which is later needed,
1112
+ // to signal that all child properties have been added
1113
+ const parentStackEntry = {
1114
+ signalChildrenFinished: true,
1115
+ initialValue: currentEntry.def.initialValue,
1116
+ propertyVarname: currentPropertyVarName,
1117
+ parentStackEntry: currentEntry.parentStackEntry,
1118
+ id: currentEntry.id,
1119
+ };
1120
+ creationStack.push(parentStackEntry);
1121
+ // Recursively add all children to the stack
1122
+ for (let [id, child] of currentEntry.def.children) {
1123
+ creationStack.push({
1124
+ parentVarName: currentPropertyVarName,
1125
+ id: id,
1126
+ def: child,
1127
+ signalParent: false,
1128
+ setGuid: currentEntry.def.assignGuid && id === 'guid',
1129
+ parentStackEntry
1130
+ });
1131
+ }
1132
+ }
1133
+ else {
1134
+ // This is a leaf property, so if there is a default value
1135
+ // we directly assign it here
1136
+ if (initialValue !== undefined) {
1137
+ if (!_.isObject(initialValue.value)) {
1138
+ // We have a primitive property and thus direclty invoke the setValue function
1139
+ creationFunctionSource +=
1140
+ `${currentPropertyVarName}.setValue(${JSON.stringify(initialValue.value)});\n`;
1141
+ }
1142
+ else {
1143
+ // For non primitive properties, we currently use the member on the property factory,
1144
+ // probably we could further optimize this to directly call the correct function on the
1145
+ // property
1146
+ creationFunctionSource +=
1147
+ `this._setInitialValue(${currentPropertyVarName},
1148
+ ${JSON.stringify(initialValue)},
1149
+ false);\n`;
1150
+ }
1151
+ }
1152
+ // If this property is constant, we assign the constant flag
1153
+ if (currentEntry.def.constant) {
1154
+ creationFunctionSource += `${currentPropertyVarName}._setAsConstant();\n`;
1155
+ }
1156
+ // If necessary, signal that the propert has been fully initialized (is this ever needed?)
1157
+ if (currentEntry.def.signal) {
1158
+ creationFunctionSource += `${currentPropertyVarName}._signalAllStaticMembersHaveBeenAdded(
1159
+ ${JSON.stringify(in_scope)}
1160
+ );\n`;
1161
+ }
1162
+ }
1163
+ }
1164
+ }
1165
+ // Add the return statement at the end of the function
1166
+ creationFunctionSource += ` return ${resultVarName};`;
1167
+ // Finally, create the actual JS function with the source we compiled above
1168
+ let creationFunction = new Function('parameters', ' GuidUtils', creationFunctionSource).bind(this, parameters, GuidUtils);
1169
+ // Add the created function to the cache
1170
+ let scopesFunction = this._cachedCreationFunctions.get(in_typeid);
1171
+ if (!scopesFunction) {
1172
+ scopesFunction = new Map();
1173
+ this._cachedCreationFunctions.set(in_typeid, scopesFunction);
1174
+ }
1175
+ let contextsFunction = scopesFunction.get(in_scope);
1176
+ if (!contextsFunction) {
1177
+ contextsFunction = new Map();
1178
+ scopesFunction.set(in_scope, contextsFunction);
1179
+ }
1180
+ contextsFunction.set(in_context, creationFunction);
1181
+ return creationFunction;
1182
+ }
844
1183
  /**
845
1184
  * Sets a value to a property
846
1185
  * The value can be passed through a default, initial or constant.
@@ -851,8 +1190,10 @@ class PropertyFactory {
851
1190
  * @param {boolean} typed - Whether the value has a different type than the property (polymorphic).
852
1191
  * @param {string} typeid - THe typeid of the property.
853
1192
  */
854
- _setInitialValue(property, valueParsed) {
855
- property._unsetAsConstant();
1193
+ _setInitialValue(property, valueParsed, unsetConstant) {
1194
+ if (unsetConstant) {
1195
+ property._unsetAsConstant();
1196
+ }
856
1197
  if (property instanceof ValueProperty || property instanceof StringProperty) {
857
1198
  property.setValue(valueParsed.value);
858
1199
  }
@@ -879,14 +1220,11 @@ class PropertyFactory {
879
1220
  * Accepted values are "single" (default), "array", "map" and "set".
880
1221
  * @param {object=} in_initialProperties A set of initial values for the PropertySet being created
881
1222
  * @param {object=} in_options Additional options
882
- * @param {property-properties.Workspace} [in_options.workspace] A checked out workspace to check against. If supplied,
883
- * the function will check against the schemas that have been registered within the workspace
884
- * @throws if the property does not have a unique id.
885
- * @throws if the property has a typeid that is not registered.
1223
+ *
886
1224
  * @return {property-properties.BaseProperty|undefined} the property instance
887
1225
  */
888
1226
  create(in_typeid, in_context, in_initialProperties) {
889
- return this._createProperty(in_typeid, in_context, in_initialProperties, null, true);
1227
+ return this._createProperty(in_typeid, in_context, in_initialProperties, null);
890
1228
  }
891
1229
  ;
892
1230
  /**
@@ -934,13 +1272,11 @@ class PropertyFactory {
934
1272
  }
935
1273
  ;
936
1274
  /**
937
- * Creates a property object that serves as parent for the template with the given typeid, when none has yet
938
- * been created,
1275
+ * Creates a property definition for a non-collection property with the entry and constructor function assigned
1276
+ * Children will be added later by parseTemplate.
939
1277
  *
940
1278
  * @param {string} in_typeid - The type unique identifier
941
1279
  * @param {string} in_id - The id of the property to create
942
- * @param {property-properties.BaseProperty} in_parent - The parent property object. If
943
- * it exists it will be returned
944
1280
  * @param {property-properties.PropertyTemplate|object|property-properties.BaseProperty} in_templateOrConstructor -
945
1281
  * the Template/Property for this in_typeid
946
1282
  * @param {string|undefined} in_scope - The scope in which the property typeid is defined
@@ -948,11 +1284,7 @@ class PropertyFactory {
948
1284
  * @return {property-properties.BaseProperty} The property that serves as parent for the properties in the template
949
1285
  * @private
950
1286
  */
951
- _ensurePropertyParentExists(in_typeid, in_id, in_parent, in_templateOrConstructor, in_scope) {
952
- // If we already have a parent, we just return it
953
- if (in_parent) {
954
- return in_parent;
955
- }
1287
+ _createNonCollectionPropertyDef(in_typeid, in_id, in_templateOrConstructor, in_scope, propertyDef) {
956
1288
  let ConstructorFunction;
957
1289
  const params = {
958
1290
  typeid: in_typeid,
@@ -970,15 +1302,22 @@ class PropertyFactory {
970
1302
  break;
971
1303
  case 'NodeProperty':
972
1304
  ConstructorFunction = NodeProperty;
1305
+ params.typeid = params.typeid || 'NodeProperty';
973
1306
  break;
974
1307
  case 'NamedProperty':
975
1308
  ConstructorFunction = NamedProperty;
1309
+ params.typeid = params.typeid || 'NamedProperty';
976
1310
  break;
977
1311
  default:
978
1312
  ConstructorFunction = ContainerProperty;
1313
+ params.typeid = params.typeid || 'ContainerProperty';
979
1314
  }
980
1315
  ConstructorFunction = this._getConstructorFunctionForTypeidAndID('single', in_typeid, ConstructorFunction, in_id, in_scope);
981
- return new ConstructorFunction(params);
1316
+ propertyDef.constructorFunction = ConstructorFunction;
1317
+ propertyDef.signal = true;
1318
+ propertyDef.entry = params;
1319
+ propertyDef.context = 'single';
1320
+ propertyDef.typeid = in_typeid;
982
1321
  }
983
1322
  ;
984
1323
  /**
@@ -1033,14 +1372,12 @@ class PropertyFactory {
1033
1372
  * @param {string=} [in_propertiesEntry.context] - Context in which the property is created
1034
1373
  * @param {Object=} [in_propertiesEntry.properties] - Context in which the property is created
1035
1374
  * @param {number} [in_propertiesEntry.length] - The length of an array property
1036
- * @param {property-properties.BaseProperty} in_parent - The parent property which will be used as
1037
- * the root to construct the property template
1038
1375
  * @param {string} in_scope - The scope in which the property typeid is defined
1039
1376
  * @param {string} context - The context of the property
1040
1377
  *
1041
1378
  * @return {string} The typeid.
1042
1379
  */
1043
- _computeTypeid(in_propertiesEntry, in_parent, in_scope, context) {
1380
+ _computeTypeid(in_propertiesEntry, in_scope, context) {
1044
1381
  var typeid = in_propertiesEntry.typeid;
1045
1382
  if (context === 'single') {
1046
1383
  var valueParsed = this._parseTypedValue(in_propertiesEntry, in_scope, context);
@@ -1060,7 +1397,7 @@ class PropertyFactory {
1060
1397
  }
1061
1398
  ;
1062
1399
  /**
1063
- * Create an instance of the given property from an entry in the properties list.
1400
+ * Creates a propertyDef for the given properties entry
1064
1401
  *
1065
1402
  * @param {Object} in_propertiesEntry - Describes the property object to create
1066
1403
  * @param {string=} [in_propertiesEntry.id] - The name of the property
@@ -1068,19 +1405,15 @@ class PropertyFactory {
1068
1405
  * @param {string=} [in_propertiesEntry.context] - Context in which the property is created
1069
1406
  * @param {Object=} [in_propertiesEntry.properties] - Context in which the property is created
1070
1407
  * @param {number} [in_propertiesEntry.length] - The length of an array property
1071
- * @param {property-properties.BaseProperty} in_parent - The parent property which will be used as
1072
- * the root to construct the property template
1073
- * @param {string} [in_scope] - The scope in which the property typeid is defined
1074
- * @param {boolean} [in_evaluateConstants] - If constants need to be traversed and created
1075
- *
1076
- * @return {property-properties.BaseProperty|undefined} the property instance
1408
+ * @param {string} in_scope - The scope in which the property
1409
+ * typeid is defined
1410
+ * @param {Object} out_propertyDef - The created property definition
1077
1411
  */
1078
- _createFromPropertyDeclaration(in_propertiesEntry, in_parent, in_scope, in_evaluateConstants) {
1412
+ _createDefFromPropertyDeclaration(in_propertiesEntry, in_scope, out_propertyDef) {
1079
1413
  var context = in_propertiesEntry.context !== undefined ? in_propertiesEntry.context : 'single';
1080
- var typeid = this._computeTypeid(in_propertiesEntry, in_parent, in_scope, context);
1414
+ var typeid = this._computeTypeid(in_propertiesEntry, in_scope, context);
1081
1415
  var referenceTarget = typeid === 'Reference' ?
1082
1416
  TypeIdHelper.extractReferenceTargetTypeIdFromReference(in_propertiesEntry.typeid) : undefined;
1083
- var parent = undefined;
1084
1417
  if (typeid) {
1085
1418
  if (this._isRegisteredTypeid(typeid, in_scope) &&
1086
1419
  (!referenceTarget || this._hasCorrespondingRegisteredTypeid(referenceTarget, in_scope))) {
@@ -1091,6 +1424,11 @@ class PropertyFactory {
1091
1424
  if (TypeIdHelper.isReferenceTypeId(typeid) || in_propertiesEntry.id !== undefined) {
1092
1425
  templateOrConstructor = this._getConstructorFunctionForTypeidAndID(in_propertiesEntry.context, in_propertiesEntry.typeid, templateOrConstructor, in_propertiesEntry.id, in_scope);
1093
1426
  }
1427
+ out_propertyDef.constructorFunction = templateOrConstructor;
1428
+ out_propertyDef.signal = false;
1429
+ out_propertyDef.entry = in_propertiesEntry;
1430
+ out_propertyDef.context = in_propertiesEntry.context;
1431
+ out_propertyDef.typeid = in_propertiesEntry.typeid;
1094
1432
  // If this is a primitive type, we create it via the registered constructor
1095
1433
  var result = new templateOrConstructor(in_propertiesEntry); // eslint-disable-line new-cap
1096
1434
  return result;
@@ -1101,23 +1439,24 @@ class PropertyFactory {
1101
1439
  if (context === 'single') {
1102
1440
  // If we have a template in a single context, we create it directly here
1103
1441
  // Create the base object
1104
- parent = this._ensurePropertyParentExists(typeid, in_propertiesEntry.id, in_parent, templateOrConstructor, in_scope);
1105
- this._parseTemplate(templateOrConstructor, parent, in_scope, !!(templateOrConstructor.inherits), in_evaluateConstants);
1442
+ this._createNonCollectionPropertyDef(typeid, in_propertiesEntry.id, templateOrConstructor, in_scope, out_propertyDef);
1443
+ this._parseTemplate(templateOrConstructor, in_scope, !!(templateOrConstructor.inherits), out_propertyDef);
1106
1444
  }
1107
1445
  else {
1108
1446
  // If we have other contexts, we have to create the corresponding property object for that context
1109
1447
  // check if a specialized collection is needed
1110
1448
  var isEnum = this.inheritsFrom(typeid, 'Enum', { scope: in_scope });
1111
- var result;
1449
+ var constructorFunction;
1112
1450
  switch (context) {
1113
1451
  case 'array':
1114
1452
  if (isEnum) {
1115
1453
  var enumPropertyEntry = deepCopy(in_propertiesEntry);
1116
1454
  enumPropertyEntry._enumDictionary = templateOrConstructor._enumDictionary;
1117
- result = new EnumArrayProperty(enumPropertyEntry);
1455
+ in_propertiesEntry = enumPropertyEntry;
1456
+ constructorFunction = EnumArrayProperty;
1118
1457
  }
1119
1458
  else {
1120
- result = new ArrayProperty(in_propertiesEntry, in_scope);
1459
+ constructorFunction = ArrayProperty;
1121
1460
  }
1122
1461
  break;
1123
1462
  case 'set':
@@ -1126,15 +1465,19 @@ class PropertyFactory {
1126
1465
  if (!this.inheritsFrom(typeid, 'NamedProperty', { scope: in_scope })) {
1127
1466
  throw new Error(MSG.SET_ONLY_NAMED_PROPS + typeid);
1128
1467
  }
1129
- result = new SetProperty(in_propertiesEntry, in_scope);
1468
+ constructorFunction = SetProperty;
1130
1469
  break;
1131
1470
  case 'map':
1132
- result = new MapProperty(in_propertiesEntry, in_scope);
1471
+ constructorFunction = MapProperty;
1133
1472
  break;
1134
1473
  default:
1135
1474
  throw new Error(MSG.UNKNOWN_CONTEXT_SPECIFIED + context);
1136
1475
  }
1137
- return result;
1476
+ out_propertyDef.constructorFunction = constructorFunction;
1477
+ out_propertyDef.signal = false;
1478
+ out_propertyDef.entry = in_propertiesEntry;
1479
+ out_propertyDef.typeid = typeid;
1480
+ out_propertyDef.context = context;
1138
1481
  }
1139
1482
  }
1140
1483
  }
@@ -1150,18 +1493,20 @@ class PropertyFactory {
1150
1493
  if (!in_propertiesEntry.properties) {
1151
1494
  in_propertiesEntry.properties = [];
1152
1495
  }
1153
- if (!parent) {
1154
- // If this is a declaration which contains a properties list, we have to create a new base property for it
1155
- parent = new ContainerProperty(in_propertiesEntry);
1156
- }
1496
+ // If this is a declaration which contains a properties list, we have to create a new container property for it
1497
+ let copiedPropertyEntry = Object.assign({ typeid: 'ContainerProperty' }, in_propertiesEntry);
1498
+ out_propertyDef.constructorFunction = ContainerProperty;
1499
+ out_propertyDef.entry = copiedPropertyEntry;
1500
+ out_propertyDef.signal = false;
1501
+ out_propertyDef.typeid = copiedPropertyEntry.typeid;
1502
+ out_propertyDef.context = 'single';
1157
1503
  // And then parse the entry like a template
1158
- this._parseTemplate(in_propertiesEntry, parent, in_scope, false, in_evaluateConstants);
1504
+ this._parseTemplate(in_propertiesEntry, in_scope, false, out_propertyDef);
1159
1505
  }
1160
1506
  // If this property inherits from NamedProperty we assign a random GUID
1161
1507
  if (typeid && this.inheritsFrom(typeid, 'NamedProperty', { scope: in_scope })) {
1162
- parent.get('guid', { referenceResolutionMode: BaseProperty.REFERENCE_RESOLUTION.NEVER }).value = GuidUtils.generateGUID();
1508
+ out_propertyDef.assignGuid = true;
1163
1509
  }
1164
- return parent;
1165
1510
  }
1166
1511
  ;
1167
1512
  /**
@@ -1219,17 +1564,16 @@ class PropertyFactory {
1219
1564
  }
1220
1565
  ;
1221
1566
  /**
1222
- * Parse a given property template appending its property and constant objects to the given property parent object
1567
+ * Parse a given property template appending its property and constant objects to the given propertyDef
1223
1568
  *
1224
1569
  * @param {property-properties.PropertyTemplate} in_template - template for the property
1225
- * @param {property-properties.BaseProperty} in_parent - the parent
1226
1570
  * @param {string} in_scope - The scope in which in_template is defined in
1227
1571
  * @param {boolean} in_allowChildMerges - Whether merging of children (nested properties) is allowed.
1228
1572
  * This is used for extending inherited properties.
1229
- * @param {boolean} [in_evaluateConstants] - If constants need to be traversed and created
1573
+ * @param {Object} out_propertyDef - The created property definition
1230
1574
  * @private
1231
1575
  */
1232
- _parseTemplate(in_template, in_parent, in_scope, in_allowChildMerges, in_evaluateConstants) {
1576
+ _parseTemplate(in_template, in_scope, in_allowChildMerges, propertyDef) {
1233
1577
  // Check if there are nested property arrays
1234
1578
  if (!(in_template.inherits && in_template.inherits.indexOf('Enum') !== -1)) {
1235
1579
  if (in_template.properties) {
@@ -1241,35 +1585,45 @@ class PropertyFactory {
1241
1585
  const optional = properties[i].optional || false;
1242
1586
  const valueParsed = this._parseTypedValue(properties[i], in_scope, context);
1243
1587
  if (optional) {
1244
- in_parent._addOptionalChild(id, typeid);
1588
+ propertyDef.optionalChildren = propertyDef.optionalChildren || {};
1589
+ propertyDef.entry.optionalChildren = true;
1590
+ propertyDef.optionalChildren[id] = typeid;
1245
1591
  }
1246
1592
  if (valueParsed.value) {
1247
- const property = this._createFromPropertyDeclaration(properties[i], undefined, in_scope, false);
1248
- this._setInitialValue(property, valueParsed);
1249
- if (optional) {
1250
- in_parent._insert(property.getId(), property, true);
1251
- }
1252
- else {
1253
- in_parent._append(property, in_allowChildMerges);
1254
- }
1593
+ const newChildEntry = {
1594
+ initialValue: valueParsed,
1595
+ optional,
1596
+ allowChildMerges: in_allowChildMerges
1597
+ };
1598
+ propertyDef.children = propertyDef.children || [];
1599
+ propertyDef.children.unshift([properties[i].id, newChildEntry]);
1600
+ this._createDefFromPropertyDeclaration(properties[i], in_scope, newChildEntry);
1255
1601
  }
1256
1602
  else if (!optional) {
1257
- const property = this._createFromPropertyDeclaration(properties[i], undefined, in_scope, false);
1258
- in_parent._append(property, in_allowChildMerges);
1603
+ const newChildEntry = {
1604
+ initialValue: undefined
1605
+ };
1606
+ propertyDef.children = propertyDef.children || [];
1607
+ propertyDef.children.unshift([properties[i].id, newChildEntry]);
1608
+ this._createDefFromPropertyDeclaration(properties[i], in_scope, newChildEntry);
1259
1609
  }
1260
1610
  }
1261
1611
  }
1262
- if (in_evaluateConstants && in_template.constants) {
1612
+ if (in_template.constants) {
1263
1613
  const constants = in_template.constants;
1264
1614
  for (let i = 0; i < constants.length; i++) {
1265
1615
  const context = constants[i].context || 'single';
1266
1616
  const valueParsed = this._parseTypedValue(constants[i], in_scope, context);
1267
- const constant = this._createFromPropertyDeclaration(constants[i], undefined, in_scope, true);
1617
+ const newChildEntry = {
1618
+ initialValue: undefined,
1619
+ constant: true
1620
+ };
1621
+ propertyDef.children = propertyDef.children || [];
1622
+ propertyDef.children.unshift([constants[i].id, newChildEntry]);
1623
+ this._createDefFromPropertyDeclaration(constants[i], in_scope, newChildEntry);
1268
1624
  if (valueParsed.value) {
1269
- this._setInitialValue(constant, valueParsed);
1625
+ newChildEntry.initialValue = valueParsed;
1270
1626
  }
1271
- constant._setAsConstant();
1272
- in_parent._append(constant, false);
1273
1627
  }
1274
1628
  }
1275
1629
  }
@@ -1436,6 +1790,8 @@ class PropertyFactory {
1436
1790
  delete this._typedPropertyConstructorCache[registeredConstructors[i]];
1437
1791
  }
1438
1792
  }
1793
+ // Remove from typeid creation cache
1794
+ this._cachedCreationFunctions.delete(typeid);
1439
1795
  // And repeat the registration
1440
1796
  registerLocal.call(this, in_template);
1441
1797
  }