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