ModdleCopy.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. import {
  2. find,
  3. forEach,
  4. isArray,
  5. isDefined,
  6. isObject,
  7. matchPattern,
  8. reduce,
  9. has,
  10. sortBy
  11. } from 'min-dash';
  12. var DISALLOWED_PROPERTIES = [
  13. 'artifacts',
  14. 'dataInputAssociations',
  15. 'dataOutputAssociations',
  16. 'default',
  17. 'flowElements',
  18. 'lanes',
  19. 'incoming',
  20. 'outgoing'
  21. ];
  22. /**
  23. * @typedef {Function} <moddleCopy.canCopyProperties> listener
  24. *
  25. * @param {Object} context
  26. * @param {Array<string>} context.propertyNames
  27. * @param {ModdleElement} context.sourceElement
  28. * @param {ModdleElement} context.targetElement
  29. *
  30. * @returns {Array<string>|boolean} - Return properties to be copied or false to disallow
  31. * copying.
  32. */
  33. /**
  34. * @typedef {Function} <moddleCopy.canCopyProperty> listener
  35. *
  36. * @param {Object} context
  37. * @param {ModdleElement} context.parent
  38. * @param {*} context.property
  39. * @param {string} context.propertyName
  40. *
  41. * @returns {*|boolean} - Return copied property or false to disallow
  42. * copying.
  43. */
  44. /**
  45. * @typedef {Function} <moddleCopy.canSetCopiedProperty> listener
  46. *
  47. * @param {Object} context
  48. * @param {ModdleElement} context.parent
  49. * @param {*} context.property
  50. * @param {string} context.propertyName
  51. *
  52. * @returns {boolean} - Return false to disallow
  53. * setting copied property.
  54. */
  55. /**
  56. * Utility for copying model properties from source element to target element.
  57. *
  58. * @param {EventBus} eventBus
  59. * @param {BpmnFactory} bpmnFactory
  60. * @param {BpmnModdle} moddle
  61. */
  62. export default function ModdleCopy(eventBus, bpmnFactory, moddle) {
  63. this._bpmnFactory = bpmnFactory;
  64. this._eventBus = eventBus;
  65. this._moddle = moddle;
  66. // copy extension elements last
  67. eventBus.on('moddleCopy.canCopyProperties', function(context) {
  68. var propertyNames = context.propertyNames;
  69. if (!propertyNames || !propertyNames.length) {
  70. return;
  71. }
  72. return sortBy(propertyNames, function(propertyName) {
  73. return propertyName === 'extensionElements';
  74. });
  75. });
  76. // default check whether property can be copied
  77. eventBus.on('moddleCopy.canCopyProperty', function(context) {
  78. var parent = context.parent,
  79. parentDescriptor = isObject(parent) && parent.$descriptor,
  80. propertyName = context.propertyName;
  81. if (propertyName && DISALLOWED_PROPERTIES.indexOf(propertyName) !== -1) {
  82. // disallow copying property
  83. return false;
  84. }
  85. if (propertyName &&
  86. parentDescriptor &&
  87. !find(parentDescriptor.properties, matchPattern({ name: propertyName }))) {
  88. // disallow copying property
  89. return false;
  90. }
  91. });
  92. // do NOT allow to copy empty extension elements
  93. eventBus.on('moddleCopy.canSetCopiedProperty', function(context) {
  94. var property = context.property;
  95. if (is(property, 'bpmn:ExtensionElements') && (!property.values || !property.values.length)) {
  96. // disallow setting copied property
  97. return false;
  98. }
  99. });
  100. }
  101. ModdleCopy.$inject = [
  102. 'eventBus',
  103. 'bpmnFactory',
  104. 'moddle'
  105. ];
  106. /**
  107. * Copy model properties of source element to target element.
  108. *
  109. * @param {ModdleElement} sourceElement
  110. * @param {ModdleElement} targetElement
  111. * @param {Array<string>} [propertyNames]
  112. *
  113. * @param {ModdleElement}
  114. */
  115. ModdleCopy.prototype.copyElement = function(sourceElement, targetElement, propertyNames) {
  116. var self = this;
  117. if (propertyNames && !isArray(propertyNames)) {
  118. propertyNames = [ propertyNames ];
  119. }
  120. propertyNames = propertyNames || getPropertyNames(sourceElement.$descriptor);
  121. var canCopyProperties = this._eventBus.fire('moddleCopy.canCopyProperties', {
  122. propertyNames: propertyNames,
  123. sourceElement: sourceElement,
  124. targetElement: targetElement
  125. });
  126. if (canCopyProperties === false) {
  127. return targetElement;
  128. }
  129. if (isArray(canCopyProperties)) {
  130. propertyNames = canCopyProperties;
  131. }
  132. // copy properties
  133. forEach(propertyNames, function(propertyName) {
  134. var sourceProperty;
  135. if (has(sourceElement, propertyName)) {
  136. sourceProperty = sourceElement.get(propertyName);
  137. }
  138. var copiedProperty = self.copyProperty(sourceProperty, targetElement, propertyName);
  139. var canSetProperty = self._eventBus.fire('moddleCopy.canSetCopiedProperty', {
  140. parent: targetElement,
  141. property: copiedProperty,
  142. propertyName: propertyName
  143. });
  144. if (canSetProperty === false) {
  145. return;
  146. }
  147. if (isDefined(copiedProperty)) {
  148. targetElement.set(propertyName, copiedProperty);
  149. }
  150. });
  151. return targetElement;
  152. };
  153. /**
  154. * Copy model property.
  155. *
  156. * @param {*} property
  157. * @param {ModdleElement} parent
  158. * @param {string} propertyName
  159. *
  160. * @returns {*}
  161. */
  162. ModdleCopy.prototype.copyProperty = function(property, parent, propertyName) {
  163. var self = this;
  164. // allow others to copy property
  165. var copiedProperty = this._eventBus.fire('moddleCopy.canCopyProperty', {
  166. parent: parent,
  167. property: property,
  168. propertyName: propertyName
  169. });
  170. // return if copying is NOT allowed
  171. if (copiedProperty === false) {
  172. return;
  173. }
  174. if (copiedProperty) {
  175. if (isObject(copiedProperty) && copiedProperty.$type && !copiedProperty.$parent) {
  176. copiedProperty.$parent = parent;
  177. }
  178. return copiedProperty;
  179. }
  180. var propertyDescriptor = this._moddle.getPropertyDescriptor(parent, propertyName);
  181. // do NOT copy Ids and references
  182. if (propertyDescriptor.isId || propertyDescriptor.isReference) {
  183. return;
  184. }
  185. // copy arrays
  186. if (isArray(property)) {
  187. return reduce(property, function(childProperties, childProperty) {
  188. // recursion
  189. copiedProperty = self.copyProperty(childProperty, parent, propertyName);
  190. // copying might NOT be allowed
  191. if (copiedProperty) {
  192. copiedProperty.$parent = parent;
  193. return childProperties.concat(copiedProperty);
  194. }
  195. return childProperties;
  196. }, []);
  197. }
  198. // copy model elements
  199. if (isObject(property) && property.$type) {
  200. if (this._moddle.getElementDescriptor(property).isGeneric) {
  201. return;
  202. }
  203. copiedProperty = self._bpmnFactory.create(property.$type);
  204. copiedProperty.$parent = parent;
  205. // recursion
  206. copiedProperty = self.copyElement(property, copiedProperty);
  207. return copiedProperty;
  208. }
  209. // copy primitive properties
  210. return property;
  211. };
  212. // helpers //////////
  213. export function getPropertyNames(descriptor, keepDefaultProperties) {
  214. return reduce(descriptor.properties, function(properties, property) {
  215. if (keepDefaultProperties && property.default) {
  216. return properties;
  217. }
  218. return properties.concat(property.name);
  219. }, []);
  220. }
  221. function is(element, type) {
  222. return element && (typeof element.$instanceOf === 'function') && element.$instanceOf(type);
  223. }