/jbpm-flow/src/main/java/org/jbpm/workflow/core/node/CompositeNode.java

https://github.com/mariofusco/jbpm · Java · 480 lines · 390 code · 65 blank · 25 comment · 99 complexity · e6daf59d32785b1a7d0ede53a0b0d6a0 MD5 · raw file

  1. /**
  2. * Copyright 2010 JBoss Inc
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.jbpm.workflow.core.node;
  17. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import org.kie.api.definition.process.Connection;
  23. import org.kie.api.definition.process.Node;
  24. import org.jbpm.workflow.core.NodeContainer;
  25. import org.jbpm.workflow.core.impl.ConnectionImpl;
  26. import org.jbpm.workflow.core.impl.NodeContainerImpl;
  27. import org.jbpm.workflow.core.impl.NodeImpl;
  28. /**
  29. *
  30. * @author <a href="mailto:kris_verlaenen@hotmail.com">Kris Verlaenen</a>
  31. */
  32. public class CompositeNode extends StateBasedNode implements NodeContainer, EventNodeInterface {
  33. private static final long serialVersionUID = 510l;
  34. private org.jbpm.workflow.core.NodeContainer nodeContainer;
  35. private Map<String, CompositeNode.NodeAndType> inConnectionMap = new HashMap<String, CompositeNode.NodeAndType>();
  36. private Map<String, CompositeNode.NodeAndType> outConnectionMap = new HashMap<String, CompositeNode.NodeAndType>();
  37. private boolean cancelRemainingInstances = true;
  38. public CompositeNode() {
  39. this.nodeContainer = new NodeContainerImpl();
  40. }
  41. public Node getNode(long id) {
  42. return nodeContainer.getNode(id);
  43. }
  44. public Node internalGetNode(long id) {
  45. return getNode(id);
  46. }
  47. public Node[] getNodes() {
  48. List<Node> subNodes = new ArrayList<Node>();
  49. for (Node node: nodeContainer.getNodes()) {
  50. if (!(node instanceof CompositeNode.CompositeNodeStart) &&
  51. !(node instanceof CompositeNode.CompositeNodeEnd)) {
  52. subNodes.add(node);
  53. }
  54. }
  55. return subNodes.toArray(new Node[subNodes.size()]);
  56. }
  57. public Node[] internalGetNodes() {
  58. return getNodes();
  59. }
  60. public void addNode(Node node) {
  61. // TODO find a more elegant solution for this
  62. // preferrable remove id setting from this class
  63. // and delegate to GUI command that drops node
  64. if (node.getId() <= 0) {
  65. long id = 0;
  66. for (Node n: nodeContainer.getNodes()) {
  67. if (n.getId() > id) {
  68. id = n.getId();
  69. }
  70. }
  71. ((org.jbpm.workflow.core.Node) node).setId(++id);
  72. }
  73. nodeContainer.addNode(node);
  74. ((org.jbpm.workflow.core.Node) node).setNodeContainer(this);
  75. }
  76. protected void internalAddNode(Node node) {
  77. addNode(node);
  78. }
  79. public void removeNode(Node node) {
  80. nodeContainer.removeNode(node);
  81. ((org.jbpm.workflow.core.Node) node).setNodeContainer(null);
  82. }
  83. protected void internalRemoveNode(Node node) {
  84. removeNode(node);
  85. }
  86. public boolean acceptsEvent(String type, Object event) {
  87. for (Node node: internalGetNodes()) {
  88. if (node instanceof EventNodeInterface) {
  89. if (((EventNodeInterface) node).acceptsEvent(type, event)) {
  90. return true;
  91. }
  92. }
  93. }
  94. return false;
  95. }
  96. public void linkIncomingConnections(String inType, long inNodeId, String inNodeType) {
  97. linkIncomingConnections(inType, new NodeAndType(inNodeId, inNodeType));
  98. }
  99. public void linkIncomingConnections(String inType, CompositeNode.NodeAndType inNode) {
  100. CompositeNode.NodeAndType oldNodeAndType = inConnectionMap.get(inType);
  101. if (oldNodeAndType != null) {
  102. if (oldNodeAndType.equals(inNode)) {
  103. return;
  104. } else {
  105. // remove old start nodes + connections
  106. List<Connection> oldInConnections =
  107. oldNodeAndType.getNode().getIncomingConnections(oldNodeAndType.getType());
  108. if (oldInConnections != null) {
  109. for (Connection connection: new ArrayList<Connection>(oldInConnections)) {
  110. if (connection.getFrom() instanceof CompositeNodeStart) {
  111. removeNode(connection.getFrom());
  112. ((ConnectionImpl) connection).terminate();
  113. }
  114. }
  115. }
  116. }
  117. }
  118. inConnectionMap.put(inType, inNode);
  119. if (inNode != null) {
  120. List<Connection> connections = getIncomingConnections(inType);
  121. for (Connection connection: connections) {
  122. CompositeNodeStart start = new CompositeNodeStart(connection.getFrom(), inType);
  123. internalAddNode(start);
  124. if (inNode.getNode() != null) {
  125. new ConnectionImpl(
  126. start, org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE,
  127. inNode.getNode(), inNode.getType());
  128. }
  129. }
  130. }
  131. }
  132. public void linkOutgoingConnections(long outNodeId, String outNodeType, String outType) {
  133. linkOutgoingConnections(new NodeAndType(outNodeId, outNodeType), outType);
  134. }
  135. public void linkOutgoingConnections(CompositeNode.NodeAndType outNode, String outType) {
  136. CompositeNode.NodeAndType oldNodeAndType = outConnectionMap.get(outType);
  137. if (oldNodeAndType != null) {
  138. if (oldNodeAndType.equals(outNode)) {
  139. return;
  140. } else {
  141. // remove old end nodes + connections
  142. List<Connection> oldOutConnections =
  143. oldNodeAndType.getNode().getOutgoingConnections(oldNodeAndType.getType());
  144. for (Connection connection: new ArrayList<Connection>(oldOutConnections)) {
  145. if (connection.getTo() instanceof CompositeNodeEnd) {
  146. removeNode(connection.getTo());
  147. ((ConnectionImpl) connection).terminate();
  148. }
  149. }
  150. }
  151. }
  152. outConnectionMap.put(outType, outNode);
  153. if (outNode != null) {
  154. List<Connection> connections = getOutgoingConnections(outType);
  155. for (Connection connection: connections) {
  156. CompositeNodeEnd end = new CompositeNodeEnd(connection.getTo(), outType);
  157. internalAddNode(end);
  158. if (outNode.getNode() != null) {
  159. new ConnectionImpl(
  160. outNode.getNode(), outNode.getType(),
  161. end, org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE);
  162. }
  163. }
  164. }
  165. }
  166. public CompositeNode.NodeAndType getLinkedIncomingNode(String inType) {
  167. return inConnectionMap.get(inType);
  168. }
  169. public CompositeNode.NodeAndType internalGetLinkedIncomingNode(String inType) {
  170. return inConnectionMap.get(inType);
  171. }
  172. public CompositeNode.NodeAndType getLinkedOutgoingNode(String outType) {
  173. return outConnectionMap.get(outType);
  174. }
  175. public CompositeNode.NodeAndType internalGetLinkedOutgoingNode(String outType) {
  176. return outConnectionMap.get(outType);
  177. }
  178. public Map<String, CompositeNode.NodeAndType> getLinkedIncomingNodes() {
  179. return inConnectionMap;
  180. }
  181. public Map<String, CompositeNode.NodeAndType> getLinkedOutgoingNodes() {
  182. return outConnectionMap;
  183. }
  184. public void validateAddIncomingConnection(final String type, final Connection connection) {
  185. CompositeNode.NodeAndType nodeAndType = internalGetLinkedIncomingNode(type);
  186. if (connection.getFrom().getNodeContainer() == this) {
  187. if (nodeAndType != null) {
  188. throw new IllegalArgumentException("Cannot link incoming connection type more than once: " + type);
  189. }
  190. } else {
  191. if (nodeAndType != null) {
  192. NodeImpl node = (NodeImpl) nodeAndType.getNode();
  193. if (node != null) {
  194. node.validateAddIncomingConnection(nodeAndType.getType(), connection);
  195. }
  196. }
  197. }
  198. }
  199. public void addIncomingConnection(String type, Connection connection) {
  200. if (connection.getFrom().getNodeContainer() == this) {
  201. linkOutgoingConnections(connection.getFrom().getId(), connection.getFromType(), org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE);
  202. } else {
  203. super.addIncomingConnection(type, connection);
  204. CompositeNode.NodeAndType inNode = internalGetLinkedIncomingNode(type);
  205. if (inNode != null) {
  206. CompositeNodeStart start = new CompositeNodeStart(connection.getFrom(), type);
  207. internalAddNode(start);
  208. NodeImpl node = (NodeImpl) inNode.getNode();
  209. if (node != null) {
  210. new ConnectionImpl(
  211. start, org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE,
  212. inNode.getNode(), inNode.getType());
  213. }
  214. }
  215. }
  216. }
  217. public void validateAddOutgoingConnection(final String type, final Connection connection) {
  218. CompositeNode.NodeAndType nodeAndType = internalGetLinkedOutgoingNode(type);
  219. if (connection.getTo().getNodeContainer() == this) {
  220. if (nodeAndType != null) {
  221. throw new IllegalArgumentException("Cannot link outgoing connection type more than once: " + type);
  222. }
  223. } else {
  224. if (nodeAndType != null) {
  225. NodeImpl node = (NodeImpl) nodeAndType.getNode();
  226. if (node != null) {
  227. ((NodeImpl) nodeAndType.getNode()).validateAddOutgoingConnection(nodeAndType.getType(), connection);
  228. }
  229. }
  230. }
  231. }
  232. public void addOutgoingConnection(String type, Connection connection) {
  233. if (connection.getTo().getNodeContainer() == this) {
  234. linkIncomingConnections(
  235. org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE,
  236. connection.getTo().getId(), connection.getToType());
  237. } else {
  238. super.addOutgoingConnection(type, connection);
  239. CompositeNode.NodeAndType outNode = internalGetLinkedOutgoingNode(type);
  240. if (outNode != null) {
  241. CompositeNodeEnd end = new CompositeNodeEnd(connection.getTo(), type);
  242. internalAddNode(end);
  243. NodeImpl node = (NodeImpl) outNode.getNode();
  244. if (node != null) {
  245. new ConnectionImpl(
  246. outNode.getNode(), outNode.getType(),
  247. end, org.jbpm.workflow.core.Node.CONNECTION_DEFAULT_TYPE);
  248. }
  249. }
  250. }
  251. }
  252. public void validateRemoveIncomingConnection(final String type, final Connection connection) {
  253. CompositeNode.NodeAndType nodeAndType = internalGetLinkedIncomingNode(type);
  254. if (nodeAndType != null) {
  255. for (Connection inConnection: nodeAndType.getNode().getIncomingConnections(nodeAndType.getType())) {
  256. if (((CompositeNodeStart) inConnection.getFrom()).getInNodeId() == connection.getFrom().getId()) {
  257. ((NodeImpl) nodeAndType.getNode()).validateRemoveIncomingConnection(nodeAndType.getType(), inConnection);
  258. return;
  259. }
  260. }
  261. throw new IllegalArgumentException(
  262. "Could not find internal incoming connection for node");
  263. }
  264. }
  265. public void removeIncomingConnection(String type, Connection connection) {
  266. super.removeIncomingConnection(type, connection);
  267. CompositeNode.NodeAndType nodeAndType = internalGetLinkedIncomingNode(type);
  268. if (nodeAndType != null) {
  269. for (Connection inConnection: nodeAndType.getNode().getIncomingConnections(nodeAndType.getType())) {
  270. if (((CompositeNodeStart) inConnection.getFrom()).getInNodeId() == connection.getFrom().getId()) {
  271. Node compositeNodeStart = inConnection.getFrom();
  272. ((ConnectionImpl) inConnection).terminate();
  273. internalRemoveNode(compositeNodeStart);
  274. return;
  275. }
  276. }
  277. throw new IllegalArgumentException(
  278. "Could not find internal incoming connection for node");
  279. }
  280. }
  281. public void validateRemoveOutgoingConnection(final String type, final Connection connection) {
  282. CompositeNode.NodeAndType nodeAndType = internalGetLinkedOutgoingNode(type);
  283. if (nodeAndType != null) {
  284. for (Connection outConnection: nodeAndType.getNode().getOutgoingConnections(nodeAndType.getType())) {
  285. if (((CompositeNodeEnd) outConnection.getTo()).getOutNodeId() == connection.getTo().getId()) {
  286. ((NodeImpl) nodeAndType.getNode()).validateRemoveOutgoingConnection(nodeAndType.getType(), outConnection);
  287. return;
  288. }
  289. }
  290. throw new IllegalArgumentException(
  291. "Could not find internal outgoing connection for node");
  292. }
  293. }
  294. public void removeOutgoingConnection(String type, Connection connection) {
  295. super.removeOutgoingConnection(type, connection);
  296. CompositeNode.NodeAndType nodeAndType = internalGetLinkedOutgoingNode(type);
  297. if (nodeAndType != null) {
  298. for (Connection outConnection: nodeAndType.getNode().getOutgoingConnections(nodeAndType.getType())) {
  299. if (((CompositeNodeEnd) outConnection.getTo()).getOutNodeId() == connection.getTo().getId()) {
  300. Node compositeNodeEnd = outConnection.getTo();
  301. ((ConnectionImpl) outConnection).terminate();
  302. internalRemoveNode(compositeNodeEnd);
  303. return;
  304. }
  305. }
  306. throw new IllegalArgumentException(
  307. "Could not find internal outgoing connection for node");
  308. }
  309. }
  310. public boolean isCancelRemainingInstances() {
  311. return cancelRemainingInstances;
  312. }
  313. public void setCancelRemainingInstances(boolean cancelRemainingInstances) {
  314. this.cancelRemainingInstances = cancelRemainingInstances;
  315. }
  316. public class NodeAndType implements Serializable {
  317. private static final long serialVersionUID = 510l;
  318. private long nodeId;
  319. private String type;
  320. private transient Node node;
  321. public NodeAndType(long nodeId, String type) {
  322. if (type == null) {
  323. throw new IllegalArgumentException(
  324. "Node or type may not be null!");
  325. }
  326. this.nodeId = nodeId;
  327. this.type = type;
  328. }
  329. public NodeAndType(Node node, String type) {
  330. if (node == null || type == null) {
  331. throw new IllegalArgumentException(
  332. "Node or type may not be null!");
  333. }
  334. this.nodeId = node.getId();
  335. this.node = node;
  336. this.type = type;
  337. }
  338. public Node getNode() {
  339. if (node == null) {
  340. try {
  341. node = nodeContainer.getNode(nodeId);
  342. } catch (IllegalArgumentException e) {
  343. // unknown node id, returning null
  344. }
  345. }
  346. return node;
  347. }
  348. public long getNodeId() {
  349. return nodeId;
  350. }
  351. public String getType() {
  352. return type;
  353. }
  354. public boolean equals(Object o) {
  355. if (o instanceof NodeAndType) {
  356. return nodeId == ((NodeAndType) o).nodeId
  357. && type.equals(((NodeAndType) o).type);
  358. }
  359. return false;
  360. }
  361. public int hashCode() {
  362. return 7*(int)nodeId + 13*type.hashCode();
  363. }
  364. }
  365. public class CompositeNodeStart extends NodeImpl {
  366. private static final long serialVersionUID = 510l;
  367. private long inNodeId;
  368. private transient Node inNode;
  369. private String inType;
  370. public CompositeNodeStart(Node outNode, String outType) {
  371. setName("Composite node start");
  372. this.inNodeId = outNode.getId();
  373. this.inNode = outNode;
  374. this.inType = outType;
  375. setMetaData("hidden", true);
  376. }
  377. public Node getInNode() {
  378. if (inNode == null) {
  379. inNode = ((NodeContainer) CompositeNode.this.getNodeContainer()).internalGetNode(inNodeId);
  380. }
  381. return inNode;
  382. }
  383. public long getInNodeId() {
  384. return inNodeId;
  385. }
  386. public String getInType() {
  387. return inType;
  388. }
  389. }
  390. public class CompositeNodeEnd extends NodeImpl {
  391. private static final long serialVersionUID = 510l;
  392. private long outNodeId;
  393. private transient Node outNode;
  394. private String outType;
  395. public CompositeNodeEnd(Node outNode, String outType) {
  396. setName("Composite node end");
  397. this.outNodeId = outNode.getId();
  398. this.outNode = outNode;
  399. this.outType = outType;
  400. setMetaData("hidden", true);
  401. }
  402. public Node getOutNode() {
  403. if (outNode == null) {
  404. outNode = ((NodeContainer) CompositeNode.this.getNodeContainer()).internalGetNode(outNodeId);
  405. }
  406. return outNode;
  407. }
  408. public long getOutNodeId() {
  409. return outNodeId;
  410. }
  411. public String getOutType() {
  412. return outType;
  413. }
  414. }
  415. }