();
+ hisActInstList.addAll(historicActivityInstances);
+
+ getHighlightedFlows(processDefinition.getActivities(), hisActInstList, highLightedFlows);
+
+ return highLightedFlows;
+ }
+
+ /**
+ * getHighlightedFlows
+ *
+ * code logic:
+ * 1. Loop all activities by id asc order;
+ * 2. Check each activity's outgoing transitions and eventBoundery outgoing transitions, if outgoing transitions's destination.id is in other executed activityIds, add this transition to highLightedFlows List;
+ * 3. But if activity is not a parallelGateway or inclusiveGateway, only choose the earliest flow.
+ *
+ * @param activityList
+ * @param hisActInstList
+ * @param highLightedFlows
+ */
+ private void getHighlightedFlows(List activityList, LinkedList hisActInstList, List highLightedFlows) {
+
+ //check out startEvents in activityList
+ List startEventActList = new ArrayList();
+ Map activityMap = new HashMap(activityList.size());
+ for (ActivityImpl activity : activityList) {
+
+ activityMap.put(activity.getId(), activity);
+
+ String actType = (String) activity.getProperty("type");
+ if (actType != null && actType.toLowerCase().indexOf("startevent") >= 0) {
+ startEventActList.add(activity);
+ }
+ }
+
+ //These codes is used to avoid a bug:
+ //ACT-1728 If the process instance was started by a callActivity, it will be not have the startEvent activity in ACT_HI_ACTINST table
+ //Code logic:
+ //Check the first activity if it is a startEvent, if not check out the startEvent's highlight outgoing flow.
+ HistoricActivityInstance firstHistActInst = hisActInstList.getFirst();
+ String firstActType = (String) firstHistActInst.getActivityType();
+ if (firstActType != null && firstActType.toLowerCase().indexOf("startevent") < 0) {
+ PvmTransition startTrans = getStartTransaction(startEventActList, firstHistActInst);
+ if (startTrans != null) {
+ highLightedFlows.add(startTrans.getId());
+ }
+ }
+
+ while (!hisActInstList.isEmpty()) {
+ HistoricActivityInstance histActInst = hisActInstList.removeFirst();
+ ActivityImpl activity = activityMap.get(histActInst.getActivityId());
+ if (activity != null) {
+ boolean isParallel = false;
+ String type = histActInst.getActivityType();
+ if ("parallelGateway".equals(type) || "inclusiveGateway".equals(type)) {
+ isParallel = true;
+ } else if ("subProcess".equals(histActInst.getActivityType())) {
+ getHighlightedFlows(activity.getActivities(), hisActInstList, highLightedFlows);
+ }
+
+ List allOutgoingTrans = new ArrayList();
+ allOutgoingTrans.addAll(activity.getOutgoingTransitions());
+ allOutgoingTrans.addAll(getBoundaryEventOutgoingTransitions(activity));
+ List activityHighLightedFlowIds = getHighlightedFlows(allOutgoingTrans, hisActInstList, isParallel);
+ highLightedFlows.addAll(activityHighLightedFlowIds);
+ }
+ }
+ }
+
+ /**
+ * Check out the outgoing transition connected to firstActInst from startEventActList
+ *
+ * @param startEventActList
+ * @param firstActInst
+ * @return
+ */
+ private PvmTransition getStartTransaction(List startEventActList, HistoricActivityInstance firstActInst) {
+ for (ActivityImpl startEventAct : startEventActList) {
+ for (PvmTransition trans : startEventAct.getOutgoingTransitions()) {
+ if (trans.getDestination().getId().equals(firstActInst.getActivityId())) {
+ return trans;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * getBoundaryEventOutgoingTransitions
+ *
+ * @param activity
+ * @return
+ */
+ private List getBoundaryEventOutgoingTransitions(ActivityImpl activity) {
+ List boundaryTrans = new ArrayList();
+ for (ActivityImpl subActivity : activity.getActivities()) {
+ String type = (String) subActivity.getProperty("type");
+ if (type != null && type.toLowerCase().indexOf("boundary") >= 0) {
+ boundaryTrans.addAll(subActivity.getOutgoingTransitions());
+ }
+ }
+ return boundaryTrans;
+ }
+
+ /**
+ * find out single activity's highlighted flowIds
+ *
+ * @param activity
+ * @param hisActInstList
+ * @param isExclusive if true only return one flowId(Such as exclusiveGateway, BoundaryEvent On Task)
+ * @return
+ */
+ private List getHighlightedFlows(List pvmTransitionList, LinkedList hisActInstList, boolean isParallel) {
+
+ List highLightedFlowIds = new ArrayList();
+
+ PvmTransition earliestTrans = null;
+ HistoricActivityInstance earliestHisActInst = null;
+
+ for (PvmTransition pvmTransition : pvmTransitionList) {
+
+ String destActId = pvmTransition.getDestination().getId();
+ HistoricActivityInstance destHisActInst = findHisActInst(hisActInstList, destActId);
+ if (destHisActInst != null) {
+ if (isParallel) {
+ highLightedFlowIds.add(pvmTransition.getId());
+ } else if (earliestHisActInst == null || (earliestHisActInst.getId().compareTo(destHisActInst.getId()) > 0)) {
+ earliestTrans = pvmTransition;
+ earliestHisActInst = destHisActInst;
+ }
+ }
+ }
+
+ if ((!isParallel) && earliestTrans != null) {
+ highLightedFlowIds.add(earliestTrans.getId());
+ }
+
+ return highLightedFlowIds;
+ }
+
+ private HistoricActivityInstance findHisActInst(LinkedList hisActInstList, String actId) {
+ for (HistoricActivityInstance hisActInst : hisActInstList) {
+ if (hisActInst.getActivityId().equals(actId)) {
+ return hisActInst;
+ }
+ }
+ return null;
+ }
}