diff --git a/len-activiti/src/main/java/com/len/actlistener/ActStartNodeListener.java b/len-activiti/src/main/java/com/len/actlistener/ActStartNodeListener.java
new file mode 100644
index 0000000..a04f1be
--- /dev/null
+++ b/len-activiti/src/main/java/com/len/actlistener/ActStartNodeListener.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2018 lenos
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.len.actlistener;
+
+import com.len.entity.ActAssignee;
+import com.len.entity.BaseTask;
+import com.len.entity.UserLeave;
+import com.len.service.ActAssigneeService;
+import com.len.service.impl.ActAssigneeServiceImpl;
+import com.len.util.AssigneeType;
+import com.len.util.SpringUtil;
+import org.activiti.engine.delegate.DelegateTask;
+import org.activiti.engine.delegate.TaskListener;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author zhuxiaomeng
+ * @date 2018/1/20.
+ * @email 154040976@qq.com
+ *
+ * 流程监听器 动态注入节点办理人
+ */
+public class ActStartNodeListener implements TaskListener {
+
+ @Override
+ public void notify(DelegateTask delegateTask) {
+ //KEY
+ String nodeId = delegateTask.getTaskDefinitionKey();
+
+ Map variables = delegateTask.getVariables();
+ BaseTask baseTask = (BaseTask) variables.get("baseTask");
+
+ delegateTask.setAssignee(baseTask.getUserId());
+
+ }
+}
diff --git a/len-activiti/src/main/java/com/len/config/ActivitiConfig.java b/len-activiti/src/main/java/com/len/config/ActivitiConfig.java
index 65af39b..bc37a6d 100644
--- a/len-activiti/src/main/java/com/len/config/ActivitiConfig.java
+++ b/len-activiti/src/main/java/com/len/config/ActivitiConfig.java
@@ -15,6 +15,8 @@ import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
+import org.activiti.image.HMProcessDiagramGenerator;
+import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Value;
@@ -53,6 +55,8 @@ public class ActivitiConfig {
processEngineConfiguration.setAnnotationFontName("宋体");
processEngineConfiguration.setLabelFontName("宋体");
+ processEngineConfiguration.setProcessDiagramGenerator(new DefaultProcessDiagramGenerator());
+
return processEngineConfiguration;
}
diff --git a/len-activiti/src/main/java/com/len/controller/UserLeaveController.java b/len-activiti/src/main/java/com/len/controller/UserLeaveController.java
index 104f8e2..dc7cb53 100644
--- a/len-activiti/src/main/java/com/len/controller/UserLeaveController.java
+++ b/len-activiti/src/main/java/com/len/controller/UserLeaveController.java
@@ -16,28 +16,29 @@
package com.len.controller;
import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.len.base.BaseController;
import com.len.base.CurrentUser;
import com.len.core.shiro.ShiroUtil;
+import com.len.entity.BaseTask;
import com.len.entity.LeaveOpinion;
+import com.len.entity.SysRoleUser;
import com.len.entity.UserLeave;
import com.len.exception.MyException;
+import com.len.service.RoleUserService;
import com.len.service.UserLeaveService;
-import com.len.util.BeanUtil;
-import com.len.util.CommonUtil;
-import com.len.util.JsonUtil;
-import com.len.util.ReType;
+import com.len.util.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
+import java.util.*;
import java.util.List;
-import java.util.Map;
+import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -49,9 +50,14 @@ import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricVariableUpdate;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
+import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
+import org.activiti.engine.impl.pvm.PvmTransition;
+import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
+import org.activiti.image.HMProcessDiagramGenerator;
import org.activiti.image.ProcessDiagramGenerator;
+import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -62,6 +68,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
+import sun.misc.BASE64Encoder;
/**
* @author zhuxiaomeng
@@ -95,9 +102,23 @@ public class UserLeaveController extends BaseController {
@Autowired
ProcessEngineConfiguration processEngineConfiguration;
+ @Autowired
+ RoleUserService roleUserService;
+
private String leaveOpinionList = "leaveOpinionList";
+ @GetMapping(value = "showMain")
+ public String showMain(Model model) {
+ return "/dashboard/dashboard";
+ }
+
+
+ @GetMapping(value = "showMain2")
+ public String showMain2(Model model) {
+ return "/dashboard/dashboard2";
+ }
+
@GetMapping(value = "showLeave")
public String showUser(Model model) {
return "/act/leave/leaveList";
@@ -175,8 +196,8 @@ public class UserLeaveController extends BaseController {
@GetMapping("updateLeave/{taskId}")
public String updateLeave(Model model, @PathVariable String taskId) {
Map variables = taskService.getVariables(taskId);
- UserLeave leave = (UserLeave) variables.get("userLeave");
- leave = leaveService.selectByPrimaryKey(leave.getId());
+ BaseTask baseTask = (BaseTask) variables.get("baseTask");
+ UserLeave leave = leaveService.selectByPrimaryKey(baseTask.getId());
model.addAttribute("leave", leave);
model.addAttribute("taskId", taskId);
return "/act/leave/update-leave";
@@ -192,7 +213,7 @@ public class UserLeaveController extends BaseController {
leaveService.updateByPrimaryKeySelective(oldLeave);
Map variables = taskService.getVariables(taskId);
- UserLeave userLeave = (UserLeave) variables.get("userLeave");
+// UserLeave userLeave = (UserLeave) variables.get("userLeave");
Map map = new HashMap<>();
if (flag) {
map.put("flag", true);
@@ -222,14 +243,15 @@ public class UserLeaveController extends BaseController {
userLeave.setUserId(user.getId());
userLeave.setUserName(user.getUsername());
userLeave.setProcessInstanceId("2018");//模拟数据
-
leaveService.insertSelective(userLeave);
Map map = new HashMap<>();
- map.put("userLeave", userLeave);
+ userLeave.setUrlpath("/leave/readOnlyLeave/"+userLeave.getId());
+ map.put("baseTask",(BaseTask) userLeave);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("process_leave", map);
userLeave.setProcessInstanceId(processInstance.getId());
UserLeave userLeave1 = leaveService.selectByPrimaryKey(userLeave.getId());
BeanUtil.copyNotNullBean(userLeave, userLeave1);
+ userLeave1.setUrlpath("/leave/readOnlyLeave/"+userLeave.getId());
leaveService.updateByPrimaryKeySelective(userLeave1);
if (processInstance == null) {
return JsonUtil.error("未识别key");
@@ -238,6 +260,13 @@ public class UserLeaveController extends BaseController {
return j;
}
+ @GetMapping("readOnlyLeave/{billId}")
+ public String readOnlyLeave(Model model, @PathVariable String billId) {
+ UserLeave leave = leaveService.selectByPrimaryKey(billId);
+ model.addAttribute("leave", leave);
+ return "/act/leave/update-leave-readonly";
+ }
+
/**
* ---------我的任务---------
*/
@@ -250,29 +279,62 @@ public class UserLeaveController extends BaseController {
@ResponseBody
public String showTaskList(Model model, com.len.entity.Task task, String page, String limit) {
CurrentUser user = CommonUtil.getUser();
+ SysRoleUser sysRoleUser = new SysRoleUser();
+ sysRoleUser.setUserId(user.getId());
+ List userRoles = roleUserService.selectByCondition(sysRoleUser);
+ List roleString = new ArrayList();
+ for(SysRoleUser sru:userRoles)
+ {
+ roleString.add(sru.getRoleId());
+ }
List taskList = taskService.createTaskQuery().taskCandidateUser(user.getId()).list();
+ List assigneeList =taskService.createTaskQuery().taskAssignee(user.getId()).list();
+ List candidateGroup =taskService.createTaskQuery().taskCandidateGroupIn(roleString).list();
+ taskList.addAll(assigneeList);
+ taskList.addAll(candidateGroup);
List tasks = new ArrayList<>();
Map map = new HashMap<>();
com.len.entity.Task taskEntity = null;
Map> mapMap = new HashMap<>();
Map objectMap = null;
+ Set taskSet = new HashSet();
for (Task task1 : taskList) {
objectMap = new HashMap<>();
- map = taskService.getVariables(task1.getId());
- UserLeave userLeave = (UserLeave) map.get("userLeave");
+ String taskId = task1.getId();
+ if(taskSet.contains(taskId))
+ {
+ continue;
+ }
+
+ map = taskService.getVariables(taskId);
+ BaseTask userLeave = (BaseTask) map.get("baseTask");
taskEntity = new com.len.entity.Task(task1);
taskEntity.setUserName(userLeave.getUserName());
taskEntity.setReason(userLeave.getReason());
+ taskEntity.setUrlpath(userLeave.getUrlpath());
/**如果是自己*/
- if (user.getId().equals(userLeave.getUserId()) && !(boolean) map.get("flag")) {
- objectMap.put("flag", true);
+ if (user.getId().equals(userLeave.getUserId()) ) {
+ if( map.get("flag")!=null)
+ {
+ if(!(boolean) map.get("flag"))
+ {
+ objectMap.put("flag", true);
+ }else
+ {
+ objectMap.put("flag", false);
+ }
+ }else
+ {
+ objectMap.put("flag", true);
+ }
} else {
objectMap.put("flag", false);
}
mapMap.put(taskEntity.getId(), objectMap);
tasks.add(taskEntity);
+ taskSet.add(taskId);
}
return ReType.jsonStrng(taskList.size(), tasks, mapMap, "id");
}
@@ -280,18 +342,18 @@ public class UserLeaveController extends BaseController {
@GetMapping("agent/{id}")
public String agent(Model model, @PathVariable("id") String taskId) {
Map variables = taskService.getVariables(taskId);
- UserLeave userLeave = (UserLeave) variables.get("userLeave");
- userLeave = leaveService.selectByPrimaryKey(userLeave.getId());
- model.addAttribute("leave", userLeave);
+ BaseTask baseTask = (BaseTask) variables.get("baseTask");
+// UserLeave userLeave = leaveService.selectByPrimaryKey(baseTask.getId());
+ model.addAttribute("leaveUrl", baseTask.getUrlpath());
model.addAttribute("taskId", taskId);
- return "/act/task/task-agent";
+ return "/act/task/task-agent-iframe";
}
@PostMapping("agent/complete")
@ResponseBody
public JsonUtil complete(LeaveOpinion op, HttpServletRequest request) {
Map variables = taskService.getVariables(op.getTaskId());
- UserLeave userLeave = (UserLeave) variables.get("userLeave");
+
CurrentUser user = ShiroUtil.getCurrentUse();
op.setCreateTime(new Date());
op.setOpId(user.getId());
@@ -299,6 +361,22 @@ public class UserLeaveController extends BaseController {
JsonUtil j = new JsonUtil();
Map map = new HashMap<>();
map.put("flag", op.isFlag());
+
+ //判断节点是否已经拒绝过一次了
+ Object needend = variables.get("needend");
+ if(needend!=null && (boolean ) needend && (!op.isFlag()) )
+ {
+ map.put("needfinish",-1); //结束
+ }else
+ {
+ if(op.isFlag())
+ {
+ map.put("needfinish",1);//通过下一个节点
+ }else
+ {
+ map.put("needfinish",0);//不通过
+ }
+ }
//审批信息叠加
List leaveList = new ArrayList<>();
Object o = variables.get(leaveOpinionList);
@@ -317,7 +395,7 @@ public class UserLeaveController extends BaseController {
/**
* 追踪图片成图
- *
+ * 增加历史流程
* @param request
* @param resp
* @param processInstanceId
@@ -326,45 +404,180 @@ public class UserLeaveController extends BaseController {
@GetMapping("getProcImage")
public void getProcImage(HttpServletRequest request, HttpServletResponse resp, String processInstanceId)
throws IOException {
+ InputStream imageStream = generateStream(request,resp,processInstanceId,true);
+ if(imageStream==null)
+ {
+ return;
+ }
+ InputStream imageNoCurrentStream = generateStream(request,resp,processInstanceId,false);
+ if(imageNoCurrentStream==null)
+ {
+ return;
+ }
+
+ AnimatedGifEncoder e = new AnimatedGifEncoder();
+ e.setTransparent(Color.BLACK);
+ e.setRepeat(0);
+ e.setQuality(19);
+ e.start(resp.getOutputStream());
+
+ BufferedImage current = ImageIO.read(imageStream); // 读入需要播放的jpg文件
+ e.addFrame(current); //添加到帧中
+
+ e.setDelay(200); //设置播放的延迟时间
+ BufferedImage nocurrent = ImageIO.read(imageNoCurrentStream); // 读入需要播放的jpg文件
+ e.addFrame(nocurrent); //添加到帧中
+
+ e.finish();
+
+// byte[] b = new byte[1024];
+// int len;
+// while ((len = imageStream.read(b, 0, 1024)) != -1) {
+// resp.getOutputStream().write(b, 0, len);
+// }
+ }
+
+ //只读图片页面
+ @GetMapping("shinePics/{processInstanceId}")
+ public String shinePics(Model model, @PathVariable String processInstanceId) {
+ model.addAttribute("processInstanceId", processInstanceId);
+ return "/act/leave/shinePics";
+ }
+
+ @GetMapping("getShineProcImage")
+ @ResponseBody
+ public String getShineProcImage(HttpServletRequest request, HttpServletResponse resp, String processInstanceId)
+ throws IOException {
+ JSONObject result = new JSONObject();
+ JSONArray shineProImages = new JSONArray();
+ BASE64Encoder encoder = new BASE64Encoder();
+ InputStream imageStream = generateStream(request,resp,processInstanceId,true);
+ if(imageStream!=null)
+ {
+ String imageCurrentNode = Base64Utils.ioToBase64(imageStream);
+ if(StringUtils.isNotBlank(imageCurrentNode))
+ {
+ shineProImages.add(imageCurrentNode);
+ }
+ }
+ InputStream imageNoCurrentStream = generateStream(request,resp,processInstanceId,false);
+ if(imageNoCurrentStream!=null)
+ {
+ String imageNoCurrentNode = Base64Utils.ioToBase64(imageNoCurrentStream);
+ if(StringUtils.isNotBlank(imageNoCurrentNode))
+ {
+ shineProImages.add(imageNoCurrentNode);
+ }
+ }
+ result.put("id",UUID.randomUUID().toString());
+ result.put("errorNo",0);
+ result.put("images",shineProImages);
+ return result.toJSONString();
+ }
+
+
+
+ public InputStream generateStream(HttpServletRequest request, HttpServletResponse resp, String processInstanceId,boolean needCurrent)
+ {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
String processDefinitionId = null;
List executedActivityIdList = new ArrayList();
+ List currentActivityIdList = new ArrayList<>();
+ List historicActivityInstanceList = new ArrayList<>();
if (processInstance != null) {
processDefinitionId = processInstance.getProcessDefinitionId();
- executedActivityIdList = this.runtimeService.getActiveActivityIds(processInstance.getId());
- } else if (historicProcessInstance != null) {
- processDefinitionId = historicProcessInstance.getProcessDefinitionId();
- List historicActivityInstanceList =
- historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().asc().list();
- for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
- executedActivityIdList.add(activityInstance.getActivityId());
+ if(needCurrent)
+ {
+ currentActivityIdList = this.runtimeService.getActiveActivityIds(processInstance.getId());
}
+ } if (historicProcessInstance != null) {
+ processDefinitionId = historicProcessInstance.getProcessDefinitionId();
+ historicActivityInstanceList =
+ historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().asc().list();
+ for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
+ executedActivityIdList.add(activityInstance.getActivityId());
}
+ }
if (StringUtils.isEmpty(processDefinitionId) || executedActivityIdList.isEmpty()) {
- return;
+ return null;
}
+
+ //高亮线路id集合
+ ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
+ List highLightedFlows = getHighLightedFlows(definitionEntity,historicActivityInstanceList);
+
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//List activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
processEngineConfiguration = processEngine.getProcessEngineConfiguration();
Context.setProcessEngineConfiguration((ProcessEngineConfigurationImpl) processEngineConfiguration);
- ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
+ HMProcessDiagramGenerator diagramGenerator = (HMProcessDiagramGenerator) processEngineConfiguration.getProcessDiagramGenerator();
//List activeIds = this.runtimeService.getActiveActivityIds(processInstance.getId());
InputStream imageStream = diagramGenerator.generateDiagram(
bpmnModel, "png",
- executedActivityIdList, Collections.emptyList(),
+ executedActivityIdList,highLightedFlows,
processEngine.getProcessEngineConfiguration().getActivityFontName(),
processEngine.getProcessEngineConfiguration().getLabelFontName(),
"宋体",
- null, 1.0);
- byte[] b = new byte[1024];
- int len;
- while ((len = imageStream.read(b, 0, 1024)) != -1) {
- resp.getOutputStream().write(b, 0, len);
- }
+ null, 1.0,currentActivityIdList);
+
+ return imageStream;
}
+ /**
+ * 获取需要高亮的线
+ * @param processDefinitionEntity
+ * @param historicActivityInstances
+ * @return
+ */
+ private List getHighLightedFlows(
+ ProcessDefinitionEntity processDefinitionEntity,
+ List historicActivityInstances) {
+
+ List highFlows = new ArrayList();// 用以保存高亮的线flowId
+ for (int i = 0; i < historicActivityInstances.size() - 1; i++) {// 对历史流程节点进行遍历
+ ActivityImpl activityImpl = processDefinitionEntity
+ .findActivity(historicActivityInstances.get(i)
+ .getActivityId());// 得到节点定义的详细信息
+ List sameStartTimeNodes = new ArrayList();// 用以保存后需开始时间相同的节点
+ ActivityImpl sameActivityImpl1 = processDefinitionEntity
+ .findActivity(historicActivityInstances.get(i + 1)
+ .getActivityId());
+ // 将后面第一个节点放在时间相同节点的集合里
+ sameStartTimeNodes.add(sameActivityImpl1);
+ for (int j = i + 1; j < historicActivityInstances.size() - 1; j++) {
+ HistoricActivityInstance activityImpl1 = historicActivityInstances
+ .get(j);// 后续第一个节点
+ HistoricActivityInstance activityImpl2 = historicActivityInstances
+ .get(j + 1);// 后续第二个节点
+ if (activityImpl1.getStartTime().equals(
+ activityImpl2.getStartTime())) {
+ // 如果第一个节点和第二个节点开始时间相同保存
+ ActivityImpl sameActivityImpl2 = processDefinitionEntity
+ .findActivity(activityImpl2.getActivityId());
+ sameStartTimeNodes.add(sameActivityImpl2);
+ } else {
+ // 有不相同跳出循环
+ break;
+ }
+ }
+ List pvmTransitions = activityImpl
+ .getOutgoingTransitions();// 取出节点的所有出去的线
+ for (PvmTransition pvmTransition : pvmTransitions) {
+ // 对所有的线进行遍历
+ ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition
+ .getDestination();
+ // 如果取出的线的目标节点存在时间相同的节点里,保存该线的id,进行高亮显示
+ if (sameStartTimeNodes.contains(pvmActivityImpl)) {
+ highFlows.add(pvmTransition.getId());
+ }
+ }
+ }
+ return highFlows;
+ }
+
+
}
diff --git a/len-activiti/src/main/java/com/len/entity/BaseTask.java b/len-activiti/src/main/java/com/len/entity/BaseTask.java
new file mode 100644
index 0000000..99b901b
--- /dev/null
+++ b/len-activiti/src/main/java/com/len/entity/BaseTask.java
@@ -0,0 +1,222 @@
+package com.len.entity;
+
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * Created by kingdee-001 on 2018/6/30.
+ */
+@MappedSuperclass
+public abstract class BaseTask implements Serializable{
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO,generator = "JDBC")
+ protected String id;
+
+ /**
+ * @return id
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * @param id
+ */
+ public void setId(String id) {
+ this.id = id == null ? null : id.trim();
+ }
+
+ @Column(name = "user_id")
+ protected String userId;
+
+ @Column(name = "user_name")
+ protected String userName;
+
+
+
+ @Column(name = "process_instance_Id")
+ protected String processInstanceId;
+
+ protected String status;
+
+ @Column(name = "create_date")
+ protected Date createDate;
+
+ @Column(name = "create_by")
+ protected String createBy;
+
+ @Column(name = "update_date")
+ protected Date updateDate;
+
+ @Column(name = "update_by")
+ protected String updateBy;
+
+
+ protected String reason;
+
+
+ //***实时节点信息
+ protected String taskName;
+
+
+ private String urlpath;
+
+
+ private Integer submittimes;
+
+
+
+
+ /**
+ * @return user_id
+ */
+ public String getUserId() {
+ return userId;
+ }
+
+ /**
+ * @param userId
+ */
+ public void setUserId(String userId) {
+ this.userId = userId == null ? null : userId.trim();
+ }
+
+ /**
+ * @return user_name
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * @param userName
+ */
+ public void setUserName(String userName) {
+ this.userName = userName == null ? null : userName.trim();
+ }
+
+
+ /**
+ * @return reason
+ */
+ public String getReason() {
+ return reason;
+ }
+
+ /**
+ * @param reason
+ */
+ public void setReason(String reason) {
+ this.reason = reason == null ? null : reason.trim();
+ }
+
+ /**
+ * @return process_instance_Id
+ */
+ public String getProcessInstanceId() {
+ return processInstanceId;
+ }
+
+ /**
+ * @param processInstanceId
+ */
+ public void setProcessInstanceId(String processInstanceId) {
+ this.processInstanceId = processInstanceId == null ? null : processInstanceId.trim();
+ }
+
+ /**
+ * @return status
+ */
+ public String getStatus() {
+ return status;
+ }
+
+ /**
+ * @param status
+ */
+ public void setStatus(String status) {
+ this.status = status == null ? null : status.trim();
+ }
+
+ /**
+ * @return create_date
+ */
+ public Date getCreateDate() {
+ return createDate;
+ }
+
+ /**
+ * @param createDate
+ */
+ public void setCreateDate(Date createDate) {
+ this.createDate = createDate;
+ }
+
+ /**
+ * @return create_by
+ */
+ public String getCreateBy() {
+ return createBy;
+ }
+
+ /**
+ * @param createBy
+ */
+ public void setCreateBy(String createBy) {
+ this.createBy = createBy == null ? null : createBy.trim();
+ }
+
+ /**
+ * @return update_date
+ */
+ public Date getUpdateDate() {
+ return updateDate;
+ }
+
+ /**
+ * @param updateDate
+ */
+ public void setUpdateDate(Date updateDate) {
+ this.updateDate = updateDate;
+ }
+
+ /**
+ * @return update_by
+ */
+ public String getUpdateBy() {
+ return updateBy;
+ }
+
+ /**
+ * @param updateBy
+ */
+ public void setUpdateBy(String updateBy) {
+ this.updateBy = updateBy == null ? null : updateBy.trim();
+ }
+
+ public String getTaskName() {
+ return taskName;
+ }
+
+ public void setTaskName(String taskName) {
+ this.taskName = taskName;
+ }
+
+ public String getUrlpath() {
+ return urlpath;
+ }
+
+ public void setUrlpath(String urlpath) {
+ this.urlpath = urlpath;
+ }
+
+ public Integer getSubmittimes() {
+ return submittimes;
+ }
+
+ public void setSubmittimes(Integer submittimes) {
+ this.submittimes = submittimes;
+ }
+}
diff --git a/len-activiti/src/main/java/com/len/entity/Task.java b/len-activiti/src/main/java/com/len/entity/Task.java
index 20f5eaa..1939a17 100644
--- a/len-activiti/src/main/java/com/len/entity/Task.java
+++ b/len-activiti/src/main/java/com/len/entity/Task.java
@@ -43,7 +43,7 @@ public class Task {
private String userName;
private String reason;
-
+ private String urlpath;
public Task() {
}
public Task(org.activiti.engine.task.Task t) {
diff --git a/len-activiti/src/main/java/com/len/entity/UserLeave.java b/len-activiti/src/main/java/com/len/entity/UserLeave.java
index ccbeeb3..12c645d 100644
--- a/len-activiti/src/main/java/com/len/entity/UserLeave.java
+++ b/len-activiti/src/main/java/com/len/entity/UserLeave.java
@@ -7,16 +7,29 @@ import java.util.List;
import javax.persistence.*;
@Table(name = "user_leave")
-public class UserLeave implements Serializable {
+public class UserLeave extends BaseTask {
+
@Id
- @GeneratedValue(generator = "JDBC")
- private String id;
+ @GeneratedValue(strategy = GenerationType.AUTO,generator = "JDBC")
+ protected String id;
- @Column(name = "user_id")
- private String userId;
+ /**
+ * @return id
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
- @Column(name = "user_name")
- private String userName;
+ /**
+ * @param id
+ */
+ @Override
+ public void setId(String id) {
+ this.id = id == null ? null : id.trim();
+ }
+
+ private Integer days;
@Column(name = "begin_time")
private Date beginTime;
@@ -24,10 +37,6 @@ public class UserLeave implements Serializable {
@Column(name = "end_time")
private Date endTime;
- private String reason;
-
- private Integer days;
-
@Column(name = "process_instance_Id")
private String processInstanceId;
@@ -49,13 +58,7 @@ public class UserLeave implements Serializable {
@Transient
private String taskName;
- public String getTaskName() {
- return taskName;
- }
- public void setTaskName(String taskName) {
- this.taskName = taskName;
- }
//请假单审核信息
private List opinionList=new ArrayList<>();
@@ -76,89 +79,6 @@ public class UserLeave implements Serializable {
}
- /**
- * @return id
- */
- public String getId() {
- return id;
- }
-
- /**
- * @param id
- */
- public void setId(String id) {
- this.id = id == null ? null : id.trim();
- }
-
- /**
- * @return user_id
- */
- public String getUserId() {
- return userId;
- }
-
- /**
- * @param userId
- */
- public void setUserId(String userId) {
- this.userId = userId == null ? null : userId.trim();
- }
-
- /**
- * @return user_name
- */
- public String getUserName() {
- return userName;
- }
-
- /**
- * @param userName
- */
- public void setUserName(String userName) {
- this.userName = userName == null ? null : userName.trim();
- }
-
- /**
- * @return begin_time
- */
- public Date getBeginTime() {
- return beginTime;
- }
-
- /**
- * @param beginTime
- */
- public void setBeginTime(Date beginTime) {
- this.beginTime = beginTime;
- }
-
- /**
- * @return end_time
- */
- public Date getEndTime() {
- return endTime;
- }
-
- /**
- * @param endTime
- */
- public void setEndTime(Date endTime) {
- this.endTime = endTime;
- }
-
- /**
- * @return reason
- */
- public String getReason() {
- return reason;
- }
-
- /**
- * @param reason
- */
- public void setReason(String reason) {
- this.reason = reason == null ? null : reason.trim();
- }
/**
* @return days
@@ -174,87 +94,24 @@ public class UserLeave implements Serializable {
this.days = days;
}
- /**
- * @return process_instance_Id
- */
- public String getProcessInstanceId() {
- return processInstanceId;
+
+
+ public Date getBeginTime() {
+ return beginTime;
}
- /**
- * @param processInstanceId
- */
- public void setProcessInstanceId(String processInstanceId) {
- this.processInstanceId = processInstanceId == null ? null : processInstanceId.trim();
+
+ public void setBeginTime(Date beginTime) {
+ this.beginTime = beginTime;
}
- /**
- * @return status
- */
- public String getStatus() {
- return status;
+
+ public Date getEndTime() {
+ return endTime;
}
- /**
- * @param status
- */
- public void setStatus(String status) {
- this.status = status == null ? null : status.trim();
- }
- /**
- * @return create_date
- */
- public Date getCreateDate() {
- return createDate;
- }
-
- /**
- * @param createDate
- */
- public void setCreateDate(Date createDate) {
- this.createDate = createDate;
- }
-
- /**
- * @return create_by
- */
- public String getCreateBy() {
- return createBy;
- }
-
- /**
- * @param createBy
- */
- public void setCreateBy(String createBy) {
- this.createBy = createBy == null ? null : createBy.trim();
- }
-
- /**
- * @return update_date
- */
- public Date getUpdateDate() {
- return updateDate;
- }
-
- /**
- * @param updateDate
- */
- public void setUpdateDate(Date updateDate) {
- this.updateDate = updateDate;
- }
-
- /**
- * @return update_by
- */
- public String getUpdateBy() {
- return updateBy;
- }
-
- /**
- * @param updateBy
- */
- public void setUpdateBy(String updateBy) {
- this.updateBy = updateBy == null ? null : updateBy.trim();
+ public void setEndTime(Date endTime) {
+ this.endTime = endTime;
}
}
\ No newline at end of file
diff --git a/len-activiti/src/main/java/com/len/util/AnimatedGifEncoder.java b/len-activiti/src/main/java/com/len/util/AnimatedGifEncoder.java
new file mode 100644
index 0000000..62fef8a
--- /dev/null
+++ b/len-activiti/src/main/java/com/len/util/AnimatedGifEncoder.java
@@ -0,0 +1,1294 @@
+package com.len.util;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Class AnimatedGifEncoder - Encodes a GIF file consisting of one or more
+ * frames.
+ *
+ *
+ * Example:
+ * AnimatedGifEncoder e = new AnimatedGifEncoder();
+ * e.start(outputFileName);
+ * e.setDelay(1000); // 1 frame per sec
+ * e.addFrame(image1);
+ * e.addFrame(image2);
+ * e.finish();
+ *
+ *
+ * No copyright asserted on the source code of this class. May be used for any
+ * purpose, however, refer to the Unisys LZW patent for restrictions on use of
+ * the associated LZWEncoder class. Please forward any corrections to
+ * kweiner@fmsware.com.
+ *
+ * @author Kevin Weiner, FM Software
+ * @version 1.03 November 2003
+ *
+ */
+
+public class AnimatedGifEncoder {
+
+ protected int width; // image size
+
+ protected int height;
+
+ protected Color transparent = null; // transparent color if given
+
+ protected int transIndex; // transparent index in color table
+
+ protected int repeat = -1; // no repeat
+
+ protected int delay = 0; // frame delay (hundredths)
+
+ protected boolean started = false; // ready to output frames
+
+ protected OutputStream out;
+
+ protected BufferedImage image; // current frame
+
+ protected byte[] pixels; // BGR byte array from frame
+
+ protected byte[] indexedPixels; // converted frame indexed to palette
+
+ protected int colorDepth; // number of bit planes
+
+ protected byte[] colorTab; // RGB palette
+
+ protected boolean[] usedEntry = new boolean[256]; // active palette entries
+
+ protected int palSize = 7; // color table size (bits-1)
+
+ protected int dispose = -1; // disposal code (-1 = use default)
+
+ protected boolean closeStream = false; // close stream when finished
+
+ protected boolean firstFrame = true;
+
+ protected boolean sizeSet = false; // if false, get size from first frame
+
+ protected int sample = 10; // default sample interval for quantizer
+
+ /**
+ * Sets the delay time between each frame, or changes it for subsequent frames
+ * (applies to last frame added).
+ *
+ * @param ms
+ * int delay time in milliseconds
+ */
+ public void setDelay(int ms) {
+ delay = Math.round(ms / 10.0f);
+ }
+
+ /**
+ * Sets the GIF frame disposal code for the last added frame and any
+ * subsequent frames. Default is 0 if no transparent color has been set,
+ * otherwise 2.
+ *
+ * @param code
+ * int disposal code.
+ */
+ public void setDispose(int code) {
+ if (code >= 0) {
+ dispose = code;
+ }
+ }
+
+ /**
+ * Sets the number of times the set of GIF frames should be played. Default is
+ * 1; 0 means play indefinitely. Must be invoked before the first image is
+ * added.
+ *
+ * @param iter
+ * int number of iterations.
+ * @return
+ */
+ public void setRepeat(int iter) {
+ if (iter >= 0) {
+ repeat = iter;
+ }
+ }
+
+ /**
+ * Sets the transparent color for the last added frame and any subsequent
+ * frames. Since all colors are subject to modification in the quantization
+ * process, the color in the final palette for each frame closest to the given
+ * color becomes the transparent color for that frame. May be set to null to
+ * indicate no transparent color.
+ *
+ * @param c
+ * Color to be treated as transparent on display.
+ */
+ public void setTransparent(Color c) {
+ transparent = c;
+ }
+
+ /**
+ * Adds next GIF frame. The frame is not written immediately, but is actually
+ * deferred until the next frame is received so that timing data can be
+ * inserted. Invoking finish()
flushes all frames. If
+ * setSize
was not invoked, the size of the first image is used
+ * for all subsequent frames.
+ *
+ * @param im
+ * BufferedImage containing frame to write.
+ * @return true if successful.
+ */
+ public boolean addFrame(BufferedImage im) {
+ if ((im == null) || !started) {
+ return false;
+ }
+ boolean ok = true;
+ try {
+ if (!sizeSet) {
+ // use first frame's size
+ setSize(im.getWidth(), im.getHeight());
+ }
+ image = im;
+ getImagePixels(); // convert to correct format if necessary
+ analyzePixels(); // build color table & map pixels
+ if (firstFrame) {
+ writeLSD(); // logical screen descriptior
+ writePalette(); // global color table
+ if (repeat >= 0) {
+ // use NS app extension to indicate reps
+ writeNetscapeExt();
+ }
+ }
+ writeGraphicCtrlExt(); // write graphic control extension
+ writeImageDesc(); // image descriptor
+ if (!firstFrame) {
+ writePalette(); // local color table
+ }
+ writePixels(); // encode and write pixel data
+ firstFrame = false;
+ } catch (IOException e) {
+ ok = false;
+ }
+
+ return ok;
+ }
+
+ /**
+ * Flushes any pending data and closes output file. If writing to an
+ * OutputStream, the stream is not closed.
+ */
+ public boolean finish() {
+ if (!started)
+ return false;
+ boolean ok = true;
+ started = false;
+ try {
+ out.write(0x3b); // gif trailer
+ out.flush();
+ if (closeStream) {
+ out.close();
+ }
+ } catch (IOException e) {
+ ok = false;
+ }
+
+ // reset for subsequent use
+ transIndex = 0;
+ out = null;
+ image = null;
+ pixels = null;
+ indexedPixels = null;
+ colorTab = null;
+ closeStream = false;
+ firstFrame = true;
+
+ return ok;
+ }
+
+ /**
+ * Sets frame rate in frames per second. Equivalent to
+ * setDelay(1000/fps)
.
+ *
+ * @param fps
+ * float frame rate (frames per second)
+ */
+ public void setFrameRate(float fps) {
+ if (fps != 0f) {
+ delay = Math.round(100f / fps);
+ }
+ }
+
+ /**
+ * Sets quality of color quantization (conversion of images to the maximum 256
+ * colors allowed by the GIF specification). Lower values (minimum = 1)
+ * produce better colors, but slow processing significantly. 10 is the
+ * default, and produces good color mapping at reasonable speeds. Values
+ * greater than 20 do not yield significant improvements in speed.
+ *
+ * @param quality
+ * int greater than 0.
+ * @return
+ */
+ public void setQuality(int quality) {
+ if (quality < 1)
+ quality = 1;
+ sample = quality;
+ }
+
+ /**
+ * Sets the GIF frame size. The default size is the size of the first frame
+ * added if this method is not invoked.
+ *
+ * @param w
+ * int frame width.
+ * @param h
+ * int frame width.
+ */
+ public void setSize(int w, int h) {
+ if (started && !firstFrame)
+ return;
+ width = w;
+ height = h;
+ if (width < 1)
+ width = 320;
+ if (height < 1)
+ height = 240;
+ sizeSet = true;
+ }
+
+ /**
+ * Initiates GIF file creation on the given stream. The stream is not closed
+ * automatically.
+ *
+ * @param os
+ * OutputStream on which GIF images are written.
+ * @return false if initial write failed.
+ */
+ public boolean start(OutputStream os) {
+ if (os == null)
+ return false;
+ boolean ok = true;
+ closeStream = false;
+ out = os;
+ try {
+ writeString("GIF89a"); // header
+ } catch (IOException e) {
+ ok = false;
+ }
+ return started = ok;
+ }
+
+ /**
+ * Initiates writing of a GIF file with the specified name.
+ *
+ * @param file
+ * String containing output file name.
+ * @return false if open or initial write failed.
+ */
+ public boolean start(String file) {
+ boolean ok = true;
+ try {
+ out = new BufferedOutputStream(new FileOutputStream(file));
+ ok = start(out);
+ closeStream = true;
+ } catch (IOException e) {
+ ok = false;
+ }
+ return started = ok;
+ }
+
+ /**
+ * Analyzes image colors and creates color map.
+ */
+ protected void analyzePixels() {
+ int len = pixels.length;
+ int nPix = len / 3;
+ indexedPixels = new byte[nPix];
+ NeuQuant nq = new NeuQuant(pixels, len, sample);
+ // initialize quantizer
+ colorTab = nq.process(); // create reduced palette
+ // convert map from BGR to RGB
+ for (int i = 0; i < colorTab.length; i += 3) {
+ byte temp = colorTab[i];
+ colorTab[i] = colorTab[i + 2];
+ colorTab[i + 2] = temp;
+ usedEntry[i / 3] = false;
+ }
+ // map image pixels to new palette
+ int k = 0;
+ for (int i = 0; i < nPix; i++) {
+ int index = nq.map(pixels[k++] & 0xff, pixels[k++] & 0xff, pixels[k++] & 0xff);
+ usedEntry[index] = true;
+ indexedPixels[i] = (byte) index;
+ }
+ pixels = null;
+ colorDepth = 8;
+ palSize = 7;
+ // get closest match to transparent color if specified
+ if (transparent != null) {
+ transIndex = findClosest(transparent);
+ }
+ }
+
+ /**
+ * Returns index of palette color closest to c
+ *
+ */
+ protected int findClosest(Color c) {
+ if (colorTab == null)
+ return -1;
+ int r = c.getRed();
+ int g = c.getGreen();
+ int b = c.getBlue();
+ int minpos = 0;
+ int dmin = 256 * 256 * 256;
+ int len = colorTab.length;
+ for (int i = 0; i < len;) {
+ int dr = r - (colorTab[i++] & 0xff);
+ int dg = g - (colorTab[i++] & 0xff);
+ int db = b - (colorTab[i] & 0xff);
+ int d = dr * dr + dg * dg + db * db;
+ int index = i / 3;
+ if (usedEntry[index] && (d < dmin)) {
+ dmin = d;
+ minpos = index;
+ }
+ i++;
+ }
+ return minpos;
+ }
+
+ /**
+ * Extracts image pixels into byte array "pixels"
+ */
+ protected void getImagePixels() {
+ int w = image.getWidth();
+ int h = image.getHeight();
+ int type = image.getType();
+ if ((w != width) || (h != height) || (type != BufferedImage.TYPE_3BYTE_BGR)) {
+ // create new image with right size/format
+ BufferedImage temp = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
+ Graphics2D g = temp.createGraphics();
+ g.drawImage(image, 0, 0, null);
+ image = temp;
+ }
+ pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
+ }
+
+ /**
+ * Writes Graphic Control Extension
+ */
+ protected void writeGraphicCtrlExt() throws IOException {
+ out.write(0x21); // extension introducer
+ out.write(0xf9); // GCE label
+ out.write(4); // data block size
+ int transp, disp;
+ if (transparent == null) {
+ transp = 0;
+ disp = 0; // dispose = no action
+ } else {
+ transp = 1;
+ disp = 2; // force clear if using transparent color
+ }
+ if (dispose >= 0) {
+ disp = dispose & 7; // user override
+ }
+ disp <<= 2;
+
+ // packed fields
+ out.write(0 | // 1:3 reserved
+ disp | // 4:6 disposal
+ 0 | // 7 user input - 0 = none
+ transp); // 8 transparency flag
+
+ writeShort(delay); // delay x 1/100 sec
+ out.write(transIndex); // transparent color index
+ out.write(0); // block terminator
+ }
+
+ /**
+ * Writes Image Descriptor
+ */
+ protected void writeImageDesc() throws IOException {
+ out.write(0x2c); // image separator
+ writeShort(0); // image position x,y = 0,0
+ writeShort(0);
+ writeShort(width); // image size
+ writeShort(height);
+ // packed fields
+ if (firstFrame) {
+ // no LCT - GCT is used for first (or only) frame
+ out.write(0);
+ } else {
+ // specify normal LCT
+ out.write(0x80 | // 1 local color table 1=yes
+ 0 | // 2 interlace - 0=no
+ 0 | // 3 sorted - 0=no
+ 0 | // 4-5 reserved
+ palSize); // 6-8 size of color table
+ }
+ }
+
+ /**
+ * Writes Logical Screen Descriptor
+ */
+ protected void writeLSD() throws IOException {
+ // logical screen size
+ writeShort(width);
+ writeShort(height);
+ // packed fields
+ out.write((0x80 | // 1 : global color table flag = 1 (gct used)
+ 0x70 | // 2-4 : color resolution = 7
+ 0x00 | // 5 : gct sort flag = 0
+ palSize)); // 6-8 : gct size
+
+ out.write(0); // background color index
+ out.write(0); // pixel aspect ratio - assume 1:1
+ }
+
+ /**
+ * Writes Netscape application extension to define repeat count.
+ */
+ protected void writeNetscapeExt() throws IOException {
+ out.write(0x21); // extension introducer
+ out.write(0xff); // app extension label
+ out.write(11); // block size
+ writeString("NETSCAPE" + "2.0"); // app id + auth code
+ out.write(3); // sub-block size
+ out.write(1); // loop sub-block id
+ writeShort(repeat); // loop count (extra iterations, 0=repeat forever)
+ out.write(0); // block terminator
+ }
+
+ /**
+ * Writes color table
+ */
+ protected void writePalette() throws IOException {
+ out.write(colorTab, 0, colorTab.length);
+ int n = (3 * 256) - colorTab.length;
+ for (int i = 0; i < n; i++) {
+ out.write(0);
+ }
+ }
+
+ /**
+ * Encodes and writes pixel data
+ */
+ protected void writePixels() throws IOException {
+ LZWEncoder encoder = new LZWEncoder(width, height, indexedPixels, colorDepth);
+ encoder.encode(out);
+ }
+
+ /**
+ * Write 16-bit value to output stream, LSB first
+ */
+ protected void writeShort(int value) throws IOException {
+ out.write(value & 0xff);
+ out.write((value >> 8) & 0xff);
+ }
+
+ /**
+ * Writes string to output stream
+ */
+ protected void writeString(String s) throws IOException {
+ for (int i = 0; i < s.length(); i++) {
+ out.write((byte) s.charAt(i));
+ }
+ }
+}
+
+//
+// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
+// K Weiner 12/00
+
+class LZWEncoder {
+
+ private static final int EOF = -1;
+
+ private int imgW, imgH;
+
+ private byte[] pixAry;
+
+ private int initCodeSize;
+
+ private int remaining;
+
+ private int curPixel;
+
+ // GIFCOMPR.C - GIF Image compression routines
+ //
+ // Lempel-Ziv compression based on 'compress'. GIF modifications by
+ // David Rowley (mgardi@watdcsu.waterloo.edu)
+
+ // General DEFINEs
+
+ static final int BITS = 12;
+
+ static final int HSIZE = 5003; // 80% occupancy
+
+ // GIF Image compression - modified 'compress'
+ //
+ // Based on: compress.c - File compression ala IEEE Computer, June 1984.
+ //
+ // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
+ // Jim McKie (decvax!mcvax!jim)
+ // Steve Davies (decvax!vax135!petsd!peora!srd)
+ // Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ // James A. Woods (decvax!ihnp4!ames!jaw)
+ // Joe Orost (decvax!vax135!petsd!joe)
+
+ int n_bits; // number of bits/code
+
+ int maxbits = BITS; // user settable max # bits/code
+
+ int maxcode; // maximum code, given n_bits
+
+ int maxmaxcode = 1 << BITS; // should NEVER generate this code
+
+ int[] htab = new int[HSIZE];
+
+ int[] codetab = new int[HSIZE];
+
+ int hsize = HSIZE; // for dynamic table sizing
+
+ int free_ent = 0; // first unused entry
+
+ // block compression parameters -- after all codes are used up,
+ // and compression rate changes, start over.
+ boolean clear_flg = false;
+
+ // Algorithm: use open addressing double hashing (no chaining) on the
+ // prefix code / next character combination. We do a variant of Knuth's
+ // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ // secondary probe. Here, the modular division first probe is gives way
+ // to a faster exclusive-or manipulation. Also do block compression with
+ // an adaptive reset, whereby the code table is cleared when the compression
+ // ratio decreases, but after the table fills. The variable-length output
+ // codes are re-sized at this point, and a special CLEAR code is generated
+ // for the decompressor. Late addition: construct the table according to
+ // file size for noticeable speed improvement on small files. Please direct
+ // questions about this implementation to ames!jaw.
+
+ int g_init_bits;
+
+ int ClearCode;
+
+ int EOFCode;
+
+ // output
+ //
+ // Output the given code.
+ // Inputs:
+ // code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ // that n_bits =< wordsize - 1.
+ // Outputs:
+ // Outputs code to the file.
+ // Assumptions:
+ // Chars are 8 bits long.
+ // Algorithm:
+ // Maintain a BITS character long buffer (so that 8 codes will
+ // fit in it exactly). Use the VAX insv instruction to insert each
+ // code in turn. When the buffer fills up empty it and start over.
+
+ int cur_accum = 0;
+
+ int cur_bits = 0;
+
+ int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF,
+ 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
+
+ // Number of characters so far in this 'packet'
+ int a_count;
+
+ // Define the storage for the packet accumulator
+ byte[] accum = new byte[256];
+
+ // ----------------------------------------------------------------------------
+ LZWEncoder(int width, int height, byte[] pixels, int color_depth) {
+ imgW = width;
+ imgH = height;
+ pixAry = pixels;
+ initCodeSize = Math.max(2, color_depth);
+ }
+
+ // Add a character to the end of the current packet, and if it is 254
+ // characters, flush the packet to disk.
+ void char_out(byte c, OutputStream outs) throws IOException {
+ accum[a_count++] = c;
+ if (a_count >= 254)
+ flush_char(outs);
+ }
+
+ // Clear out the hash table
+
+ // table clear for block compress
+ void cl_block(OutputStream outs) throws IOException {
+ cl_hash(hsize);
+ free_ent = ClearCode + 2;
+ clear_flg = true;
+
+ output(ClearCode, outs);
+ }
+
+ // reset code table
+ void cl_hash(int hsize) {
+ for (int i = 0; i < hsize; ++i)
+ htab[i] = -1;
+ }
+
+ void compress(int init_bits, OutputStream outs) throws IOException {
+ int fcode;
+ int i /* = 0 */;
+ int c;
+ int ent;
+ int disp;
+ int hsize_reg;
+ int hshift;
+
+ // Set up the globals: g_init_bits - initial number of bits
+ g_init_bits = init_bits;
+
+ // Set up the necessary values
+ clear_flg = false;
+ n_bits = g_init_bits;
+ maxcode = MAXCODE(n_bits);
+
+ ClearCode = 1 << (init_bits - 1);
+ EOFCode = ClearCode + 1;
+ free_ent = ClearCode + 2;
+
+ a_count = 0; // clear packet
+
+ ent = nextPixel();
+
+ hshift = 0;
+ for (fcode = hsize; fcode < 65536; fcode *= 2)
+ ++hshift;
+ hshift = 8 - hshift; // set hash code range bound
+
+ hsize_reg = hsize;
+ cl_hash(hsize_reg); // clear hash table
+
+ output(ClearCode, outs);
+
+ outer_loop: while ((c = nextPixel()) != EOF) {
+ fcode = (c << maxbits) + ent;
+ i = (c << hshift) ^ ent; // xor hashing
+
+ if (htab[i] == fcode) {
+ ent = codetab[i];
+ continue;
+ } else if (htab[i] >= 0) // non-empty slot
+ {
+ disp = hsize_reg - i; // secondary hash (after G. Knott)
+ if (i == 0)
+ disp = 1;
+ do {
+ if ((i -= disp) < 0)
+ i += hsize_reg;
+
+ if (htab[i] == fcode) {
+ ent = codetab[i];
+ continue outer_loop;
+ }
+ } while (htab[i] >= 0);
+ }
+ output(ent, outs);
+ ent = c;
+ if (free_ent < maxmaxcode) {
+ codetab[i] = free_ent++; // code -> hashtable
+ htab[i] = fcode;
+ } else
+ cl_block(outs);
+ }
+ // Put out the final code.
+ output(ent, outs);
+ output(EOFCode, outs);
+ }
+
+ // ----------------------------------------------------------------------------
+ void encode(OutputStream os) throws IOException {
+ os.write(initCodeSize); // write "initial code size" byte
+
+ remaining = imgW * imgH; // reset navigation variables
+ curPixel = 0;
+
+ compress(initCodeSize + 1, os); // compress and write the pixel data
+
+ os.write(0); // write block terminator
+ }
+
+ // Flush the packet to disk, and reset the accumulator
+ void flush_char(OutputStream outs) throws IOException {
+ if (a_count > 0) {
+ outs.write(a_count);
+ outs.write(accum, 0, a_count);
+ a_count = 0;
+ }
+ }
+
+ final int MAXCODE(int n_bits) {
+ return (1 << n_bits) - 1;
+ }
+
+ // ----------------------------------------------------------------------------
+ // Return the next pixel from the image
+ // ----------------------------------------------------------------------------
+ private int nextPixel() {
+ if (remaining == 0)
+ return EOF;
+
+ --remaining;
+
+ byte pix = pixAry[curPixel++];
+
+ return pix & 0xff;
+ }
+
+ void output(int code, OutputStream outs) throws IOException {
+ cur_accum &= masks[cur_bits];
+
+ if (cur_bits > 0)
+ cur_accum |= (code << cur_bits);
+ else
+ cur_accum = code;
+
+ cur_bits += n_bits;
+
+ while (cur_bits >= 8) {
+ char_out((byte) (cur_accum & 0xff), outs);
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+
+ // If the next entry is going to be too big for the code size,
+ // then increase it, if possible.
+ if (free_ent > maxcode || clear_flg) {
+ if (clear_flg) {
+ maxcode = MAXCODE(n_bits = g_init_bits);
+ clear_flg = false;
+ } else {
+ ++n_bits;
+ if (n_bits == maxbits)
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE(n_bits);
+ }
+ }
+
+ if (code == EOFCode) {
+ // At EOF, write the rest of the buffer.
+ while (cur_bits > 0) {
+ char_out((byte) (cur_accum & 0xff), outs);
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+
+ flush_char(outs);
+ }
+ }
+}
+
+/*
+ * NeuQuant Neural-Net Quantization Algorithm
+ * ------------------------------------------
+ *
+ * Copyright (c) 1994 Anthony Dekker
+ *
+ * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
+ * "Kohonen neural networks for optimal colour quantization" in "Network:
+ * Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
+ * the algorithm.
+ *
+ * Any party obtaining a copy of these files from the author, directly or
+ * indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+ * world-wide, paid up, royalty-free, nonexclusive right and license to deal in
+ * this software and documentation files (the "Software"), including without
+ * limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons who
+ * receive copies from any such party to do so, with the only requirement being
+ * that this copyright notice remain intact.
+ */
+
+// Ported to Java 12/00 K Weiner
+class NeuQuant {
+
+ protected static final int netsize = 256; /* number of colours used */
+
+ /* four primes near 500 - assume no image has a length so large */
+ /* that it is divisible by all four primes */
+ protected static final int prime1 = 499;
+
+ protected static final int prime2 = 491;
+
+ protected static final int prime3 = 487;
+
+ protected static final int prime4 = 503;
+
+ protected static final int minpicturebytes = (3 * prime4);
+
+ /* minimum size for input image */
+
+ /*
+ * Program Skeleton ---------------- [select samplefac in range 1..30] [read
+ * image from input file] pic = (unsigned char*) malloc(3*width*height);
+ * initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output
+ * image header, using writecolourmap(f)] inxbuild(); write output image using
+ * inxsearch(b,g,r)
+ */
+
+ /*
+ * Network Definitions -------------------
+ */
+
+ protected static final int maxnetpos = (netsize - 1);
+
+ protected static final int netbiasshift = 4; /* bias for colour values */
+
+ protected static final int ncycles = 100; /* no. of learning cycles */
+
+ /* defs for freq and bias */
+ protected static final int intbiasshift = 16; /* bias for fractions */
+
+ protected static final int intbias = (((int) 1) << intbiasshift);
+
+ protected static final int gammashift = 10; /* gamma = 1024 */
+
+ protected static final int gamma = (((int) 1) << gammashift);
+
+ protected static final int betashift = 10;
+
+ protected static final int beta = (intbias >> betashift); /* beta = 1/1024 */
+
+ protected static final int betagamma = (intbias << (gammashift - betashift));
+
+ /* defs for decreasing radius factor */
+ protected static final int initrad = (netsize >> 3); /*
+ * for 256 cols, radius
+ * starts
+ */
+
+ protected static final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */
+
+ protected static final int radiusbias = (((int) 1) << radiusbiasshift);
+
+ protected static final int initradius = (initrad * radiusbias); /*
+ * and
+ * decreases
+ * by a
+ */
+
+ protected static final int radiusdec = 30; /* factor of 1/30 each cycle */
+
+ /* defs for decreasing alpha factor */
+ protected static final int alphabiasshift = 10; /* alpha starts at 1.0 */
+
+ protected static final int initalpha = (((int) 1) << alphabiasshift);
+
+ protected int alphadec; /* biased by 10 bits */
+
+ /* radbias and alpharadbias used for radpower calculation */
+ protected static final int radbiasshift = 8;
+
+ protected static final int radbias = (((int) 1) << radbiasshift);
+
+ protected static final int alpharadbshift = (alphabiasshift + radbiasshift);
+
+ protected static final int alpharadbias = (((int) 1) << alpharadbshift);
+
+ /*
+ * Types and Global Variables --------------------------
+ */
+
+ protected byte[] thepicture; /* the input image itself */
+
+ protected int lengthcount; /* lengthcount = H*W*3 */
+
+ protected int samplefac; /* sampling factor 1..30 */
+
+ // typedef int pixel[4]; /* BGRc */
+ protected int[][] network; /* the network itself - [netsize][4] */
+
+ protected int[] netindex = new int[256];
+
+ /* for network lookup - really 256 */
+
+ protected int[] bias = new int[netsize];
+
+ /* bias and freq arrays for learning */
+ protected int[] freq = new int[netsize];
+
+ protected int[] radpower = new int[initrad];
+
+ /* radpower for precomputation */
+
+ /*
+ * Initialise network in range (0,0,0) to (255,255,255) and set parameters
+ * -----------------------------------------------------------------------
+ */
+ public NeuQuant(byte[] thepic, int len, int sample) {
+
+ int i;
+ int[] p;
+
+ thepicture = thepic;
+ lengthcount = len;
+ samplefac = sample;
+
+ network = new int[netsize][];
+ for (i = 0; i < netsize; i++) {
+ network[i] = new int[4];
+ p = network[i];
+ p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / netsize;
+ freq[i] = intbias / netsize; /* 1/netsize */
+ bias[i] = 0;
+ }
+ }
+
+ public byte[] colorMap() {
+ byte[] map = new byte[3 * netsize];
+ int[] index = new int[netsize];
+ for (int i = 0; i < netsize; i++)
+ index[network[i][3]] = i;
+ int k = 0;
+ for (int i = 0; i < netsize; i++) {
+ int j = index[i];
+ map[k++] = (byte) (network[j][0]);
+ map[k++] = (byte) (network[j][1]);
+ map[k++] = (byte) (network[j][2]);
+ }
+ return map;
+ }
+
+ /*
+ * Insertion sort of network and building of netindex[0..255] (to do after
+ * unbias)
+ * -------------------------------------------------------------------------------
+ */
+ public void inxbuild() {
+
+ int i, j, smallpos, smallval;
+ int[] p;
+ int[] q;
+ int previouscol, startpos;
+
+ previouscol = 0;
+ startpos = 0;
+ for (i = 0; i < netsize; i++) {
+ p = network[i];
+ smallpos = i;
+ smallval = p[1]; /* index on g */
+ /* find smallest in i..netsize-1 */
+ for (j = i + 1; j < netsize; j++) {
+ q = network[j];
+ if (q[1] < smallval) { /* index on g */
+ smallpos = j;
+ smallval = q[1]; /* index on g */
+ }
+ }
+ q = network[smallpos];
+ /* swap p (i) and q (smallpos) entries */
+ if (i != smallpos) {
+ j = q[0];
+ q[0] = p[0];
+ p[0] = j;
+ j = q[1];
+ q[1] = p[1];
+ p[1] = j;
+ j = q[2];
+ q[2] = p[2];
+ p[2] = j;
+ j = q[3];
+ q[3] = p[3];
+ p[3] = j;
+ }
+ /* smallval entry is now in position i */
+ if (smallval != previouscol) {
+ netindex[previouscol] = (startpos + i) >> 1;
+ for (j = previouscol + 1; j < smallval; j++)
+ netindex[j] = i;
+ previouscol = smallval;
+ startpos = i;
+ }
+ }
+ netindex[previouscol] = (startpos + maxnetpos) >> 1;
+ for (j = previouscol + 1; j < 256; j++)
+ netindex[j] = maxnetpos; /* really 256 */
+ }
+
+ /*
+ * Main Learning Loop ------------------
+ */
+ public void learn() {
+
+ int i, j, b, g, r;
+ int radius, rad, alpha, step, delta, samplepixels;
+ byte[] p;
+ int pix, lim;
+
+ if (lengthcount < minpicturebytes)
+ samplefac = 1;
+ alphadec = 30 + ((samplefac - 1) / 3);
+ p = thepicture;
+ pix = 0;
+ lim = lengthcount;
+ samplepixels = lengthcount / (3 * samplefac);
+ delta = samplepixels / ncycles;
+ alpha = initalpha;
+ radius = initradius;
+
+ rad = radius >> radiusbiasshift;
+ if (rad <= 1)
+ rad = 0;
+ for (i = 0; i < rad; i++)
+ radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad));
+
+ // fprintf(stderr,"beginning 1D learning: initial radius=%d\n", rad);
+
+ if (lengthcount < minpicturebytes)
+ step = 3;
+ else if ((lengthcount % prime1) != 0)
+ step = 3 * prime1;
+ else {
+ if ((lengthcount % prime2) != 0)
+ step = 3 * prime2;
+ else {
+ if ((lengthcount % prime3) != 0)
+ step = 3 * prime3;
+ else
+ step = 3 * prime4;
+ }
+ }
+
+ i = 0;
+ while (i < samplepixels) {
+ b = (p[pix + 0] & 0xff) << netbiasshift;
+ g = (p[pix + 1] & 0xff) << netbiasshift;
+ r = (p[pix + 2] & 0xff) << netbiasshift;
+ j = contest(b, g, r);
+
+ altersingle(alpha, j, b, g, r);
+ if (rad != 0)
+ alterneigh(rad, j, b, g, r); /* alter neighbours */
+
+ pix += step;
+ if (pix >= lim)
+ pix -= lengthcount;
+
+ i++;
+ if (delta == 0)
+ delta = 1;
+ if (i % delta == 0) {
+ alpha -= alpha / alphadec;
+ radius -= radius / radiusdec;
+ rad = radius >> radiusbiasshift;
+ if (rad <= 1)
+ rad = 0;
+ for (j = 0; j < rad; j++)
+ radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad));
+ }
+ }
+ // fprintf(stderr,"finished 1D learning: final alpha=%f
+ // !\n",((float)alpha)/initalpha);
+ }
+
+ /*
+ * Search for BGR values 0..255 (after net is unbiased) and return colour
+ * index
+ * ----------------------------------------------------------------------------
+ */
+ public int map(int b, int g, int r) {
+
+ int i, j, dist, a, bestd;
+ int[] p;
+ int best;
+
+ bestd = 1000; /* biggest possible dist is 256*3 */
+ best = -1;
+ i = netindex[g]; /* index on g */
+ j = i - 1; /* start at netindex[g] and work outwards */
+
+ while ((i < netsize) || (j >= 0)) {
+ if (i < netsize) {
+ p = network[i];
+ dist = p[1] - g; /* inx key */
+ if (dist >= bestd)
+ i = netsize; /* stop iter */
+ else {
+ i++;
+ if (dist < 0)
+ dist = -dist;
+ a = p[0] - b;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ a = p[2] - r;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ if (j >= 0) {
+ p = network[j];
+ dist = g - p[1]; /* inx key - reverse dif */
+ if (dist >= bestd)
+ j = -1; /* stop iter */
+ else {
+ j--;
+ if (dist < 0)
+ dist = -dist;
+ a = p[0] - b;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ a = p[2] - r;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ }
+ return (best);
+ }
+
+ public byte[] process() {
+ learn();
+ unbiasnet();
+ inxbuild();
+ return colorMap();
+ }
+
+ /*
+ * Unbias network to give byte values 0..255 and record position i to prepare
+ * for sort
+ * -----------------------------------------------------------------------------------
+ */
+ public void unbiasnet() {
+ for (int i = 0; i < netsize; i++) {
+ network[i][0] >>= netbiasshift;
+ network[i][1] >>= netbiasshift;
+ network[i][2] >>= netbiasshift;
+ network[i][3] = i; /* record colour no */
+ }
+ }
+
+ /*
+ * Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in
+ * radpower[|i-j|]
+ * ---------------------------------------------------------------------------------
+ */
+ protected void alterneigh(int rad, int i, int b, int g, int r) {
+
+ int j, k, lo, hi, a, m;
+ int[] p;
+
+ lo = i - rad;
+ if (lo < -1)
+ lo = -1;
+ hi = i + rad;
+ if (hi > netsize)
+ hi = netsize;
+
+ j = i + 1;
+ k = i - 1;
+ m = 1;
+ while ((j < hi) || (k > lo)) {
+ a = radpower[m++];
+ if (j < hi) {
+ p = network[j++];
+ try {
+ p[0] -= (a * (p[0] - b)) / alpharadbias;
+ p[1] -= (a * (p[1] - g)) / alpharadbias;
+ p[2] -= (a * (p[2] - r)) / alpharadbias;
+ } catch (Exception e) {
+ } // prevents 1.3 miscompilation
+ }
+ if (k > lo) {
+ p = network[k--];
+ try {
+ p[0] -= (a * (p[0] - b)) / alpharadbias;
+ p[1] -= (a * (p[1] - g)) / alpharadbias;
+ p[2] -= (a * (p[2] - r)) / alpharadbias;
+ } catch (Exception e) {
+ }
+ }
+ }
+ }
+
+ /*
+ * Move neuron i towards biased (b,g,r) by factor alpha
+ * ----------------------------------------------------
+ */
+ protected void altersingle(int alpha, int i, int b, int g, int r) {
+
+ /* alter hit neuron */
+ int[] n = network[i];
+ n[0] -= (alpha * (n[0] - b)) / initalpha;
+ n[1] -= (alpha * (n[1] - g)) / initalpha;
+ n[2] -= (alpha * (n[2] - r)) / initalpha;
+ }
+
+ /*
+ * Search for biased BGR values ----------------------------
+ */
+ protected int contest(int b, int g, int r) {
+
+ /* finds closest neuron (min dist) and updates freq */
+ /* finds best neuron (min dist-bias) and returns position */
+ /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */
+ /* bias[i] = gamma*((1/netsize)-freq[i]) */
+
+ int i, dist, a, biasdist, betafreq;
+ int bestpos, bestbiaspos, bestd, bestbiasd;
+ int[] n;
+
+ bestd = ~(((int) 1) << 31);
+ bestbiasd = bestd;
+ bestpos = -1;
+ bestbiaspos = bestpos;
+
+ for (i = 0; i < netsize; i++) {
+ n = network[i];
+ dist = n[0] - b;
+ if (dist < 0)
+ dist = -dist;
+ a = n[1] - g;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ a = n[2] - r;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ bestpos = i;
+ }
+ biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift));
+ if (biasdist < bestbiasd) {
+ bestbiasd = biasdist;
+ bestbiaspos = i;
+ }
+ betafreq = (freq[i] >> betashift);
+ freq[i] -= betafreq;
+ bias[i] += (betafreq << gammashift);
+ }
+ freq[bestpos] += beta;
+ bias[bestpos] -= betagamma;
+ return (bestbiaspos);
+ }
+}
\ No newline at end of file
diff --git a/len-activiti/src/main/java/com/len/util/Base64Utils.java b/len-activiti/src/main/java/com/len/util/Base64Utils.java
new file mode 100644
index 0000000..0787b47
--- /dev/null
+++ b/len-activiti/src/main/java/com/len/util/Base64Utils.java
@@ -0,0 +1,32 @@
+package com.len.util;
+
+import org.apache.log4j.Logger;
+import sun.misc.BASE64Decoder;
+import sun.misc.BASE64Encoder;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Created by user on 2018/7/12.
+ */
+public class Base64Utils {
+ private static Logger logger = Logger.getLogger(Base64Utils.class);
+ private static BASE64Encoder encoder = new BASE64Encoder();
+
+ public static String ioToBase64(InputStream in) throws IOException {
+ String strBase64 = null;
+ try {
+ byte[] bytes = new byte[in.available()];
+ // 将文件中的内容读入到数组中
+ in.read(bytes);
+ strBase64 = encoder.encode(bytes); //将字节流数组转换为字符串
+ in.close();
+ } catch (IOException ioe) {
+ logger.error("图片转64编码异常",ioe);
+ }
+ return strBase64;
+ }
+}
diff --git a/len-activiti/src/main/java/org/activiti/image/HMProcessDiagramGenerator.java b/len-activiti/src/main/java/org/activiti/image/HMProcessDiagramGenerator.java
new file mode 100644
index 0000000..72121b3
--- /dev/null
+++ b/len-activiti/src/main/java/org/activiti/image/HMProcessDiagramGenerator.java
@@ -0,0 +1,80 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.activiti.image;
+
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.util.List;
+
+import org.activiti.bpmn.model.BpmnModel;
+
+/**
+ * This interface declares methods to generate process diagram
+ *
+ * @author martin.grofcik
+ * @author Tijs Rademakers
+ */
+public interface HMProcessDiagramGenerator extends ProcessDiagramGenerator{
+
+ /**
+ * Generates a diagram of the given process definition, using the
+ * diagram interchange information of the process.
+ * @param bpmnModel bpmn model to get diagram for
+ * @param imageType type of the image to generate.
+ * @param highLightedActivities activities to highlight
+ * @param highLightedFlows flows to highlight
+ * @param activityFontName override the default activity font
+ * @param labelFontName override the default label font
+ * @param annotationFontName override the default annotation font
+ * @param customClassLoader provide a custom classloader for retrieving icon images
+ */
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor);
+
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor,List currentHighlightdActivities);
+
+ /**
+ * Generates a diagram of the given process definition, using the
+ * diagram interchange information of the process.
+ * @param bpmnModel bpmn model to get diagram for
+ * @param imageType type of the image to generate.
+ * @param highLightedActivities activities to highlight
+ * @param highLightedFlows flows to highlight
+ */
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows);
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities,
+ List highLightedFlows, double scaleFactor);
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities);
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, double scaleFactor);
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader);
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
+ String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor);
+
+ public InputStream generatePngDiagram(BpmnModel bpmnModel);
+
+ public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor);
+
+ public InputStream generateJpgDiagram(BpmnModel bpmnModel);
+
+ public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor);
+
+ public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor);
+
+}
diff --git a/len-activiti/src/main/java/org/activiti/image/impl/DefaultProcessDiagramCanvas.java b/len-activiti/src/main/java/org/activiti/image/impl/DefaultProcessDiagramCanvas.java
new file mode 100644
index 0000000..134c970
--- /dev/null
+++ b/len-activiti/src/main/java/org/activiti/image/impl/DefaultProcessDiagramCanvas.java
@@ -0,0 +1,1352 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.activiti.image.impl;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineBreakMeasurer;
+import java.awt.font.TextAttribute;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Rectangle2D;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.activiti.bpmn.model.AssociationDirection;
+import org.activiti.bpmn.model.GraphicInfo;
+import org.activiti.image.exception.ActivitiImageException;
+import org.activiti.image.util.ReflectUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Represents a canvas on which BPMN 2.0 constructs can be drawn.
+ *
+ * Some of the icons used are licensed under a Creative Commons Attribution 2.5
+ * License, see http://www.famfamfam.com/lab/icons/silk/
+ *
+ * @see org.activiti.engine.impl.bpmn.diagram.DefaultProcessDiagramGenerator
+ * @author Joram Barrez
+ */
+public class DefaultProcessDiagramCanvas {
+
+ protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultProcessDiagramCanvas.class);
+ public enum SHAPE_TYPE {Rectangle, Rhombus, Ellipse}
+
+ // Predefined sized
+ protected static final int ARROW_WIDTH = 5;
+ protected static final int CONDITIONAL_INDICATOR_WIDTH = 16;
+ protected static final int DEFAULT_INDICATOR_WIDTH = 10;
+ protected static final int MARKER_WIDTH = 12;
+ protected static final int FONT_SIZE = 11;
+ protected static final int FONT_SPACING = 2;
+ protected static final int TEXT_PADDING = 3;
+ protected static final int ANNOTATION_TEXT_PADDING = 7;
+ protected static final int LINE_HEIGHT = FONT_SIZE + FONT_SPACING;
+
+
+ // Colors
+ protected static Color TASK_BOX_COLOR = new Color(249, 249, 249);
+ protected static Color SUBPROCESS_BOX_COLOR = new Color(255, 255, 255);
+ protected static Color EVENT_COLOR = new Color(255, 255, 255);
+ protected static Color CONNECTION_COLOR = new Color(88, 88, 88);
+ protected static Color CONDITIONAL_INDICATOR_COLOR = new Color(255, 255, 255);
+ protected static Color HIGHLIGHT_COLOR = Color.GREEN;
+ protected static Color LABEL_COLOR = new Color(112, 146, 190);
+ protected static Color TASK_BORDER_COLOR = new Color(187, 187, 187);
+ protected static Color EVENT_BORDER_COLOR = new Color(88, 88, 88);
+ protected static Color SUBPROCESS_BORDER_COLOR = new Color(0, 0, 0);
+
+ // Fonts
+ protected static Font LABEL_FONT = null;
+ protected static Font ANNOTATION_FONT = null;
+
+ // Strokes
+ protected static Stroke THICK_TASK_BORDER_STROKE = new BasicStroke(3.0f);
+ protected static Stroke GATEWAY_TYPE_STROKE = new BasicStroke(3.0f);
+ protected static Stroke END_EVENT_STROKE = new BasicStroke(3.0f);
+ protected static Stroke MULTI_INSTANCE_STROKE = new BasicStroke(1.3f);
+ protected static Stroke EVENT_SUBPROCESS_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, new float[] { 1.0f }, 0.0f);
+ protected static Stroke NON_INTERRUPTING_EVENT_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, new float[] { 4.0f, 3.0f }, 0.0f);
+ protected static Stroke HIGHLIGHT_FLOW_STROKE = new BasicStroke(1.3f);
+ protected static Stroke ANNOTATION_STROKE = new BasicStroke(2.0f);
+ protected static Stroke ASSOCIATION_STROKE = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, new float[] { 2.0f, 2.0f }, 0.0f);
+
+ // icons
+ protected static int ICON_PADDING = 5;
+ protected static BufferedImage USERTASK_IMAGE;
+ protected static BufferedImage SCRIPTTASK_IMAGE;
+ protected static BufferedImage SERVICETASK_IMAGE;
+ protected static BufferedImage RECEIVETASK_IMAGE;
+ protected static BufferedImage SENDTASK_IMAGE;
+ protected static BufferedImage MANUALTASK_IMAGE;
+ protected static BufferedImage BUSINESS_RULE_TASK_IMAGE;
+ protected static BufferedImage SHELL_TASK_IMAGE;
+ protected static BufferedImage MULE_TASK_IMAGE;
+ protected static BufferedImage CAMEL_TASK_IMAGE;
+
+ protected static BufferedImage TIMER_IMAGE;
+ protected static BufferedImage COMPENSATE_THROW_IMAGE;
+ protected static BufferedImage COMPENSATE_CATCH_IMAGE;
+ protected static BufferedImage ERROR_THROW_IMAGE;
+ protected static BufferedImage ERROR_CATCH_IMAGE;
+ protected static BufferedImage MESSAGE_THROW_IMAGE;
+ protected static BufferedImage MESSAGE_CATCH_IMAGE;
+ protected static BufferedImage SIGNAL_CATCH_IMAGE;
+ protected static BufferedImage SIGNAL_THROW_IMAGE;
+
+ protected int canvasWidth = -1;
+ protected int canvasHeight = -1;
+ protected int minX = -1;
+ protected int minY = -1;
+ protected BufferedImage processDiagram;
+ protected Graphics2D g;
+ protected FontMetrics fontMetrics;
+ protected boolean closed;
+ protected ClassLoader customClassLoader;
+ protected String activityFontName = "Arial";
+ protected String labelFontName = "Arial";
+ protected String annotationFontName = "Arial";
+
+ /**
+ * Creates an empty canvas with given width and height.
+ *
+ * Allows to specify minimal boundaries on the left and upper side of the
+ * canvas. This is useful for diagrams that have white space there.
+ * Everything beneath these minimum values will be cropped.
+ * It's also possible to pass a specific font name and a class loader for the icon images.
+ *
+ */
+ public DefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
+
+ this.canvasWidth = width;
+ this.canvasHeight = height;
+ this.minX = minX;
+ this.minY = minY;
+ if (activityFontName != null) {
+ this.activityFontName = activityFontName;
+ }
+ if (labelFontName != null) {
+ this.labelFontName = labelFontName;
+ }
+ if (annotationFontName != null) {
+ this.annotationFontName = annotationFontName;
+ }
+ this.customClassLoader = customClassLoader;
+
+ initialize(imageType);
+ }
+
+ /**
+ * Creates an empty canvas with given width and height.
+ *
+ * Allows to specify minimal boundaries on the left and upper side of the
+ * canvas. This is useful for diagrams that have white space there (eg
+ * Signavio). Everything beneath these minimum values will be cropped.
+ *
+ * @param minX
+ * Hint that will be used when generating the image. Parts that fall
+ * below minX on the horizontal scale will be cropped.
+ * @param minY
+ * Hint that will be used when generating the image. Parts that fall
+ * below minX on the horizontal scale will be cropped.
+ */
+ public DefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {
+ this.canvasWidth = width;
+ this.canvasHeight = height;
+ this.minX = minX;
+ this.minY = minY;
+
+ initialize(imageType);
+ }
+
+ public void initialize(String imageType) {
+ if ("png".equalsIgnoreCase(imageType)) {
+ this.processDiagram = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB);
+ } else {
+ this.processDiagram = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_RGB);
+ }
+
+ this.g = processDiagram.createGraphics();
+ if ("png".equalsIgnoreCase(imageType) == false) {
+ this.g.setBackground(new Color(255, 255, 255, 0));
+ this.g.clearRect(0, 0, canvasWidth, canvasHeight);
+ }
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setPaint(Color.black);
+
+ Font font = new Font(activityFontName, Font.BOLD, FONT_SIZE);
+ g.setFont(font);
+ this.fontMetrics = g.getFontMetrics();
+
+ LABEL_FONT = new Font(labelFontName, Font.BOLD, 14);
+ ANNOTATION_FONT = new Font(annotationFontName, Font.PLAIN, FONT_SIZE);
+
+ try {
+ USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/userTask.png", customClassLoader));
+ SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/scriptTask.png", customClassLoader));
+ SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/serviceTask.png", customClassLoader));
+ RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/receiveTask.png", customClassLoader));
+ SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/sendTask.png", customClassLoader));
+ MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/manualTask.png", customClassLoader));
+ BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/businessRuleTask.png", customClassLoader));
+ SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/shellTask.png", customClassLoader));
+ CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/camelTask.png", customClassLoader));
+ MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/muleTask.png", customClassLoader));
+
+ TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/timer.png", customClassLoader));
+ COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate-throw.png", customClassLoader));
+ COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/compensate.png", customClassLoader));
+ ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error-throw.png", customClassLoader));
+ ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/error.png", customClassLoader));
+ MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message-throw.png", customClassLoader));
+ MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/message.png", customClassLoader));
+ SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal-throw.png", customClassLoader));
+ SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/activiti/icons/signal.png", customClassLoader));
+ } catch (IOException e) {
+ LOGGER.warn("Could not load image for process diagram creation: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * Generates an image of what currently is drawn on the canvas.
+ *
+ * Throws an {@link ActivitiException} when {@link #close()} is already
+ * called.
+ */
+ public InputStream generateImage(String imageType) {
+ if (closed) {
+ throw new ActivitiImageException("ProcessDiagramGenerator already closed");
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ ImageIO.write(processDiagram, imageType, out);
+
+ } catch (IOException e) {
+ throw new ActivitiImageException("Error while generating process image", e);
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch(IOException ignore) {
+ // Exception is silently ignored
+ }
+ }
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+
+ /**
+ * Generates an image of what currently is drawn on the canvas.
+ *
+ * Throws an {@link ActivitiException} when {@link #close()} is already
+ * called.
+ */
+ public BufferedImage generateBufferedImage(String imageType) {
+ if (closed) {
+ throw new ActivitiImageException("ProcessDiagramGenerator already closed");
+ }
+
+ // Try to remove white space
+ minX = (minX <= 5) ? 5 : minX;
+ minY = (minY <= 5) ? 5 : minY;
+ BufferedImage imageToSerialize = processDiagram;
+ if (minX >= 0 && minY >= 0) {
+ imageToSerialize = processDiagram.getSubimage(minX - 5, minY - 5, canvasWidth - minX + 5, canvasHeight - minY + 5);
+ }
+ return imageToSerialize;
+ }
+
+ /**
+ * Closes the canvas which dissallows further drawing and releases graphical
+ * resources.
+ */
+ public void close() {
+ g.dispose();
+ closed = true;
+ }
+
+ public void drawNoneStartEvent(GraphicInfo graphicInfo) {
+ drawStartEvent(graphicInfo, null, 1.0);
+ }
+
+ public void drawTimerStartEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawStartEvent(graphicInfo, TIMER_IMAGE, scaleFactor);
+ }
+
+ public void drawSignalStartEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawStartEvent(graphicInfo, SIGNAL_CATCH_IMAGE, scaleFactor);
+ }
+
+ public void drawMessageStartEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawStartEvent(graphicInfo, MESSAGE_CATCH_IMAGE, scaleFactor);
+ }
+
+ public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) {
+ Paint originalPaint = g.getPaint();
+ g.setPaint(EVENT_COLOR);
+// g.setPaint(new Color(36,202,88));
+ Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
+ graphicInfo.getWidth(), graphicInfo.getHeight());
+ g.fill(circle);
+ g.setPaint(EVENT_BORDER_COLOR);
+ g.draw(circle);
+ g.setPaint(originalPaint);
+ if (image != null) {
+ // calculate coordinates to center image
+ int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / 2 * scaleFactor));
+ int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / 2 * scaleFactor));
+ g.drawImage(image, imageX, imageY,
+ (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null);
+ }
+
+ }
+
+ public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ Paint originalPaint = g.getPaint();
+ Stroke originalStroke = g.getStroke();
+// g.setPaint(EVENT_COLOR);
+ g.setPaint(new Color(255,0,0));
+ Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
+ graphicInfo.getWidth(), graphicInfo.getHeight());
+ g.fill(circle);
+ g.setPaint(EVENT_BORDER_COLOR);
+ if (scaleFactor == 1.0) {
+ g.setStroke(END_EVENT_STROKE);
+ } else {
+ g.setStroke(new BasicStroke(2.0f));
+ }
+ g.draw(circle);
+ g.setStroke(originalStroke);
+ g.setPaint(originalPaint);
+ }
+
+ public void drawErrorEndEvent(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawErrorEndEvent(graphicInfo, scaleFactor);
+ if (scaleFactor == 1.0) {
+ drawLabel(name, graphicInfo);
+ }
+ }
+
+ public void drawErrorEndEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawNoneEndEvent(graphicInfo, scaleFactor);
+ g.drawImage(ERROR_THROW_IMAGE, (int) (graphicInfo.getX() + (graphicInfo.getWidth() / 4)),
+ (int) (graphicInfo.getY() + (graphicInfo.getHeight() / 4)),
+ (int) (ERROR_THROW_IMAGE.getWidth() / scaleFactor),
+ (int) (ERROR_THROW_IMAGE.getHeight() / scaleFactor), null);
+ }
+
+ public void drawErrorStartEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawNoneStartEvent(graphicInfo);
+ g.drawImage(ERROR_CATCH_IMAGE, (int) (graphicInfo.getX() + (graphicInfo.getWidth() / 4)),
+ (int) (graphicInfo.getY() + (graphicInfo.getHeight() / 4)),
+ (int) (ERROR_CATCH_IMAGE.getWidth() / scaleFactor),
+ (int) (ERROR_CATCH_IMAGE.getHeight() / scaleFactor), null);
+ }
+
+ public void drawCatchingEvent(GraphicInfo graphicInfo, boolean isInterrupting,
+ BufferedImage image, String eventType, double scaleFactor) {
+
+ // event circles
+ Ellipse2D outerCircle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(),
+ graphicInfo.getWidth(), graphicInfo.getHeight());
+ int innerCircleSize = (int) (4 / scaleFactor);
+ if (innerCircleSize == 0) {
+ innerCircleSize = 1;
+ }
+ int innerCircleX = (int) graphicInfo.getX() + innerCircleSize;
+ int innerCircleY = (int) graphicInfo.getY() + innerCircleSize;
+ int innerCircleWidth = (int) graphicInfo.getWidth() - (2 * innerCircleSize);
+ int innerCircleHeight = (int) graphicInfo.getHeight() - (2 * innerCircleSize);
+ Ellipse2D innerCircle = new Ellipse2D.Double(innerCircleX, innerCircleY, innerCircleWidth, innerCircleHeight);
+
+ Paint originalPaint = g.getPaint();
+ Stroke originalStroke = g.getStroke();
+ g.setPaint(EVENT_COLOR);
+ g.fill(outerCircle);
+
+ g.setPaint(EVENT_BORDER_COLOR);
+ if (isInterrupting == false)
+ g.setStroke(NON_INTERRUPTING_EVENT_STROKE);
+ g.draw(outerCircle);
+ g.setStroke(originalStroke);
+ g.setPaint(originalPaint);
+ g.draw(innerCircle);
+
+ if (image != null) {
+ // calculate coordinates to center image
+ int imageX = (int) (graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / 2 * scaleFactor));
+ int imageY = (int) (graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / 2 * scaleFactor));
+ if (scaleFactor == 1.0 && "timer".equals(eventType)) {
+ // move image one pixel to center timer image
+ imageX++;
+ imageY++;
+ }
+ g.drawImage(image, imageX, imageY, (int) (image.getWidth() / scaleFactor),
+ (int) (image.getHeight() / scaleFactor), null);
+ }
+ }
+
+ public void drawCatchingCompensateEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingCompensateEvent(graphicInfo, isInterrupting, scaleFactor);
+ drawLabel(name, graphicInfo);
+ }
+
+ public void drawCatchingCompensateEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, isInterrupting, COMPENSATE_CATCH_IMAGE, "compensate", scaleFactor);
+ }
+
+ public void drawCatchingTimerEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingTimerEvent(graphicInfo, isInterrupting, scaleFactor);
+ drawLabel(name, graphicInfo);
+ }
+
+ public void drawCatchingTimerEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, isInterrupting, TIMER_IMAGE, "timer", scaleFactor);
+ }
+
+ public void drawCatchingErrorEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingErrorEvent(graphicInfo, isInterrupting, scaleFactor);
+ drawLabel(name, graphicInfo);
+ }
+
+ public void drawCatchingErrorEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, isInterrupting, ERROR_CATCH_IMAGE, "error", scaleFactor);
+ }
+
+ public void drawCatchingSignalEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingSignalEvent(graphicInfo, isInterrupting, scaleFactor);
+ drawLabel(name, graphicInfo);
+ }
+
+ public void drawCatchingSignalEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, isInterrupting, SIGNAL_CATCH_IMAGE, "signal", scaleFactor);
+ }
+
+ public void drawCatchingMessageEvent(GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, isInterrupting, MESSAGE_CATCH_IMAGE, "message", scaleFactor);
+ }
+
+ public void drawCatchingMessageEvent(String name, GraphicInfo graphicInfo, boolean isInterrupting, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, isInterrupting, MESSAGE_CATCH_IMAGE, "message", scaleFactor);
+ drawLabel(name, graphicInfo);
+ }
+
+ public void drawThrowingCompensateEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, true, COMPENSATE_THROW_IMAGE, "compensate", scaleFactor);
+ }
+
+ public void drawThrowingSignalEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, true, SIGNAL_THROW_IMAGE, "signal", scaleFactor);
+ }
+
+ public void drawThrowingNoneEvent(GraphicInfo graphicInfo, double scaleFactor) {
+ drawCatchingEvent(graphicInfo, true, null, "none", scaleFactor);
+ }
+
+ public void drawSequenceflow(int srcX, int srcY, int targetX, int targetY, boolean conditional, double scaleFactor) {
+ drawSequenceflow(srcX, srcY, targetX, targetY, conditional, false, scaleFactor);
+ }
+
+ public void drawSequenceflow(int srcX, int srcY, int targetX, int targetY, boolean conditional, boolean highLighted, double scaleFactor) {
+ Paint originalPaint = g.getPaint();
+ if (highLighted)
+ g.setPaint(HIGHLIGHT_COLOR);
+
+ Line2D.Double line = new Line2D.Double(srcX, srcY, targetX, targetY);
+ g.draw(line);
+ drawArrowHead(line, scaleFactor);
+
+ if (conditional) {
+ drawConditionalSequenceFlowIndicator(line, scaleFactor);
+ }
+
+ if (highLighted)
+ g.setPaint(originalPaint);
+ }
+
+ public void drawAssociation(int[] xPoints, int[] yPoints, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
+ boolean conditional = false, isDefault = false;
+ drawConnection(xPoints, yPoints, conditional, isDefault, "association", associationDirection, highLighted, scaleFactor);
+ }
+
+ public void drawSequenceflow(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, boolean highLighted, double scaleFactor) {
+ drawConnection(xPoints, yPoints, conditional, isDefault, "sequenceFlow", AssociationDirection.ONE, highLighted, scaleFactor);
+ }
+
+ public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType,
+ AssociationDirection associationDirection, boolean highLighted, double scaleFactor) {
+
+ Paint originalPaint = g.getPaint();
+ Stroke originalStroke = g.getStroke();
+
+ g.setPaint(CONNECTION_COLOR);
+ if (connectionType.equals("association")) {
+ g.setStroke(ASSOCIATION_STROKE);
+ } else if (highLighted) {
+ g.setPaint(HIGHLIGHT_COLOR);
+ g.setStroke(HIGHLIGHT_FLOW_STROKE);
+ }
+
+ for (int i=1; i 1.0) return;
+ int horizontal = (int) (CONDITIONAL_INDICATOR_WIDTH * 0.7);
+ int halfOfHorizontal = horizontal / 2;
+ int halfOfVertical = CONDITIONAL_INDICATOR_WIDTH / 2;
+
+ Polygon conditionalIndicator = new Polygon();
+ conditionalIndicator.addPoint(0, 0);
+ conditionalIndicator.addPoint(-halfOfHorizontal, halfOfVertical);
+ conditionalIndicator.addPoint(0, CONDITIONAL_INDICATOR_WIDTH);
+ conditionalIndicator.addPoint(halfOfHorizontal, halfOfVertical);
+
+ AffineTransform transformation = new AffineTransform();
+ transformation.setToIdentity();
+ double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
+ transformation.translate(line.x1, line.y1);
+ transformation.rotate((angle - Math.PI / 2d));
+
+ AffineTransform originalTransformation = g.getTransform();
+ g.setTransform(transformation);
+ g.draw(conditionalIndicator);
+
+ Paint originalPaint = g.getPaint();
+ g.setPaint(CONDITIONAL_INDICATOR_COLOR);
+ g.fill(conditionalIndicator);
+
+ g.setPaint(originalPaint);
+ g.setTransform(originalTransformation);
+ }
+
+ public void drawTask(BufferedImage icon, String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(name, graphicInfo);
+ g.drawImage(icon, (int) (graphicInfo.getX() + ICON_PADDING / scaleFactor),
+ (int) (graphicInfo.getY() + ICON_PADDING / scaleFactor),
+ (int) (icon.getWidth() / scaleFactor), (int) (icon.getHeight() / scaleFactor), null);
+ }
+
+ public void drawTask(String name, GraphicInfo graphicInfo) {
+ drawTask(name, graphicInfo, false);
+ }
+
+ public void drawPoolOrLane(String name, GraphicInfo graphicInfo) {
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+ g.drawRect(x, y, width, height);
+
+ // Add the name as text, vertical
+ if(name != null && name.length() > 0) {
+ // Include some padding
+ int availableTextSpace = height - 6;
+
+ // Create rotation for derived font
+ AffineTransform transformation = new AffineTransform();
+ transformation.setToIdentity();
+ transformation.rotate(270 * Math.PI/180);
+
+ Font currentFont = g.getFont();
+ Font theDerivedFont = currentFont.deriveFont(transformation);
+ g.setFont(theDerivedFont);
+
+ String truncated = fitTextToWidth(name, availableTextSpace);
+ int realWidth = fontMetrics.stringWidth(truncated);
+
+ g.drawString(truncated, x + 2 + fontMetrics.getHeight(), 3 + y + availableTextSpace - (availableTextSpace - realWidth) / 2);
+ g.setFont(currentFont);
+ }
+ }
+
+ protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder) {
+ Paint originalPaint = g.getPaint();
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+
+ // Create a new gradient paint for every task box, gradient depends on x and y and is not relative
+// g.setPaint(TASK_BOX_COLOR);
+ g.setPaint(new Color(255,255,0));
+ int arcR = 6;
+ if (thickBorder)
+ arcR = 3;
+
+ // shape
+ RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR);
+ g.fill(rect);
+ g.setPaint(TASK_BORDER_COLOR);
+
+ if (thickBorder) {
+ Stroke originalStroke = g.getStroke();
+ g.setStroke(THICK_TASK_BORDER_STROKE);
+ g.draw(rect);
+ g.setStroke(originalStroke);
+ } else {
+ g.draw(rect);
+ }
+
+ g.setPaint(originalPaint);
+ // text
+ if (name != null && name.length() > 0) {
+ int boxWidth = width - (2 * TEXT_PADDING);
+ int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2;
+ int boxX = x + width/2 - boxWidth/2;
+ int boxY = y + height/2 - boxHeight/2 + ICON_PADDING + ICON_PADDING - 2 - 2;
+
+ drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight);
+ }
+ }
+
+ protected void drawMultilineCentredText(String text, int x, int y, int boxWidth, int boxHeight) {
+ drawMultilineText(text, x, y, boxWidth, boxHeight, true);
+ }
+
+ protected void drawMultilineAnnotationText(String text, int x, int y, int boxWidth, int boxHeight) {
+ drawMultilineText(text, x, y, boxWidth, boxHeight, false);
+ }
+
+ protected void drawMultilineText(String text, int x, int y, int boxWidth, int boxHeight, boolean centered) {
+ // Create an attributed string based in input text
+ AttributedString attributedString = new AttributedString(text);
+ attributedString.addAttribute(TextAttribute.FONT, g.getFont());
+ attributedString.addAttribute(TextAttribute.FOREGROUND, Color.black);
+
+ AttributedCharacterIterator characterIterator = attributedString.getIterator();
+
+ int currentHeight = 0;
+ // Prepare a list of lines of text we'll be drawing
+ List layouts = new ArrayList();
+ String lastLine = null;
+
+ LineBreakMeasurer measurer = new LineBreakMeasurer(characterIterator, g.getFontRenderContext());
+
+ TextLayout layout = null;
+ while (measurer.getPosition() < characterIterator.getEndIndex() && currentHeight <= boxHeight) {
+
+ int previousPosition = measurer.getPosition();
+
+ // Request next layout
+ layout = measurer.nextLayout(boxWidth);
+
+ int height = ((Float)(layout.getDescent() + layout.getAscent() + layout.getLeading())).intValue();
+
+ if(currentHeight + height > boxHeight) {
+ // The line we're about to add should NOT be added anymore, append three dots to previous one instead
+ // to indicate more text is truncated
+ if (!layouts.isEmpty()) {
+ layouts.remove(layouts.size() - 1);
+
+ if(lastLine.length() >= 4) {
+ lastLine = lastLine.substring(0, lastLine.length() - 4) + "...";
+ }
+ layouts.add(new TextLayout(lastLine, g.getFont(), g.getFontRenderContext()));
+ }
+ break;
+ } else {
+ layouts.add(layout);
+ lastLine = text.substring(previousPosition, measurer.getPosition());
+ currentHeight += height;
+ }
+ }
+
+
+ int currentY = y + (centered ? ((boxHeight - currentHeight) /2) : 0);
+ int currentX = 0;
+
+ // Actually draw the lines
+ for(TextLayout textLayout : layouts) {
+
+ currentY += textLayout.getAscent();
+ currentX = x + (centered ? ((boxWidth - ((Double)textLayout.getBounds().getWidth()).intValue()) /2) : 0);
+
+ textLayout.draw(g, currentX, currentY);
+ currentY += textLayout.getDescent() + textLayout.getLeading();
+ }
+
+ }
+
+
+ protected String fitTextToWidth(String original, int width) {
+ String text = original;
+
+ // remove length for "..."
+ int maxWidth = width - 10;
+
+ while (fontMetrics.stringWidth(text + "...") > maxWidth && text.length() > 0) {
+ text = text.substring(0, text.length() - 1);
+ }
+
+ if (!text.equals(original)) {
+ text = text + "...";
+ }
+
+ return text;
+ }
+
+ public void drawUserTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(USERTASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawScriptTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(SCRIPTTASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawServiceTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(SERVICETASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawReceiveTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(RECEIVETASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawSendTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(SENDTASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawManualTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(MANUALTASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawBusinessRuleTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(BUSINESS_RULE_TASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawCamelTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(CAMEL_TASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawMuleTask(String name, GraphicInfo graphicInfo, double scaleFactor) {
+ drawTask(MULE_TASK_IMAGE, name, graphicInfo, scaleFactor);
+ }
+
+ public void drawExpandedSubProcess(String name, GraphicInfo graphicInfo, Boolean isTriggeredByEvent, double scaleFactor) {
+ RoundRectangle2D rect = new RoundRectangle2D.Double(graphicInfo.getX(), graphicInfo.getY(),
+ graphicInfo.getWidth(), graphicInfo.getHeight(), 8, 8);
+
+ // Use different stroke (dashed)
+ if (isTriggeredByEvent) {
+ Stroke originalStroke = g.getStroke();
+ g.setStroke(EVENT_SUBPROCESS_STROKE);
+ g.draw(rect);
+ g.setStroke(originalStroke);
+ } else {
+ Paint originalPaint = g.getPaint();
+ g.setPaint(SUBPROCESS_BOX_COLOR);
+ g.fill(rect);
+ g.setPaint(SUBPROCESS_BORDER_COLOR);
+ g.draw(rect);
+ g.setPaint(originalPaint);
+ }
+
+ if (scaleFactor == 1.0 && name != null && !name.isEmpty()) {
+ String text = fitTextToWidth(name, (int) graphicInfo.getWidth());
+ g.drawString(text, (int) graphicInfo.getX() + 10, (int) graphicInfo.getY() + 15);
+ }
+ }
+
+ public void drawCollapsedSubProcess(String name, GraphicInfo graphicInfo, Boolean isTriggeredByEvent) {
+ drawCollapsedTask(name, graphicInfo, false);
+ }
+
+ public void drawCollapsedCallActivity(String name, GraphicInfo graphicInfo) {
+ drawCollapsedTask(name, graphicInfo, true);
+ }
+
+ protected void drawCollapsedTask(String name, GraphicInfo graphicInfo, boolean thickBorder) {
+ // The collapsed marker is now visualized separately
+ drawTask(name, graphicInfo, thickBorder);
+ }
+
+ public void drawCollapsedMarker(int x, int y, int width, int height) {
+ // rectangle
+ int rectangleWidth = MARKER_WIDTH;
+ int rectangleHeight = MARKER_WIDTH;
+ Rectangle rect = new Rectangle(x + (width - rectangleWidth) / 2, y + height - rectangleHeight - 3, rectangleWidth, rectangleHeight);
+ g.draw(rect);
+
+ // plus inside rectangle
+ Line2D.Double line = new Line2D.Double(rect.getCenterX(), rect.getY() + 2, rect.getCenterX(), rect.getMaxY() - 2);
+ g.draw(line);
+ line = new Line2D.Double(rect.getMinX() + 2, rect.getCenterY(), rect.getMaxX() - 2, rect.getCenterY());
+ g.draw(line);
+ }
+
+ public void drawActivityMarkers(int x, int y, int width, int height, boolean multiInstanceSequential, boolean multiInstanceParallel, boolean collapsed) {
+ if (collapsed) {
+ if (!multiInstanceSequential && !multiInstanceParallel) {
+ drawCollapsedMarker(x, y, width, height);
+ } else {
+ drawCollapsedMarker(x - MARKER_WIDTH / 2 - 2, y, width, height);
+ if (multiInstanceSequential) {
+ drawMultiInstanceMarker(true, x + MARKER_WIDTH / 2 + 2, y, width, height);
+ } else {
+ drawMultiInstanceMarker(false, x + MARKER_WIDTH / 2 + 2, y, width, height);
+ }
+ }
+ } else {
+ if (multiInstanceSequential) {
+ drawMultiInstanceMarker(true, x, y, width, height);
+ } else if (multiInstanceParallel) {
+ drawMultiInstanceMarker(false, x, y, width, height);
+ }
+ }
+ }
+
+ public void drawGateway(GraphicInfo graphicInfo) {
+ Polygon rhombus = new Polygon();
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+
+ rhombus.addPoint(x, y + (height / 2));
+ rhombus.addPoint(x + (width / 2), y + height);
+ rhombus.addPoint(x + width, y + (height / 2));
+ rhombus.addPoint(x + (width / 2), y);
+ g.draw(rhombus);
+ }
+
+ public void drawParallelGateway(GraphicInfo graphicInfo, double scaleFactor) {
+ // rhombus
+ drawGateway(graphicInfo);
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+
+ if (scaleFactor == 1.0) {
+ // plus inside rhombus
+ Stroke orginalStroke = g.getStroke();
+ g.setStroke(GATEWAY_TYPE_STROKE);
+ Line2D.Double line = new Line2D.Double(x + 10, y + height / 2, x + width - 10, y + height / 2); // horizontal
+ g.draw(line);
+ line = new Line2D.Double(x + width / 2, y + height - 10, x + width / 2, y + 10); // vertical
+ g.draw(line);
+ g.setStroke(orginalStroke);
+ }
+ }
+
+ public void drawExclusiveGateway(GraphicInfo graphicInfo, double scaleFactor) {
+ // rhombus
+ drawGateway(graphicInfo);
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+
+ int quarterWidth = width / 4;
+ int quarterHeight = height / 4;
+
+ if (scaleFactor == 1.0) {
+ // X inside rhombus
+ Stroke orginalStroke = g.getStroke();
+ g.setStroke(GATEWAY_TYPE_STROKE);
+ Line2D.Double line = new Line2D.Double(x + quarterWidth + 3, y + quarterHeight + 3, x + 3 * quarterWidth - 3, y + 3 * quarterHeight - 3);
+ g.draw(line);
+ line = new Line2D.Double(x + quarterWidth + 3, y + 3 * quarterHeight - 3, x + 3 * quarterWidth - 3, y + quarterHeight + 3);
+ g.draw(line);
+ g.setStroke(orginalStroke);
+ }
+ }
+
+ public void drawInclusiveGateway(GraphicInfo graphicInfo, double scaleFactor) {
+ // rhombus
+ drawGateway(graphicInfo);
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+
+ int diameter = width / 2;
+
+ if (scaleFactor == 1.0) {
+ // circle inside rhombus
+ Stroke orginalStroke = g.getStroke();
+ g.setStroke(GATEWAY_TYPE_STROKE);
+ Ellipse2D.Double circle = new Ellipse2D.Double(((width - diameter) / 2) + x, ((height - diameter) / 2) + y, diameter, diameter);
+ g.draw(circle);
+ g.setStroke(orginalStroke);
+ }
+ }
+
+ public void drawEventBasedGateway(GraphicInfo graphicInfo, double scaleFactor) {
+ // rhombus
+ drawGateway(graphicInfo);
+
+ if (scaleFactor == 1.0) {
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+
+ double scale = .6;
+
+ GraphicInfo eventInfo = new GraphicInfo();
+ eventInfo.setX(x + width*(1-scale)/2);
+ eventInfo.setY(y + height*(1-scale)/2);
+ eventInfo.setWidth(width*scale);
+ eventInfo.setHeight(height*scale);
+ drawCatchingEvent(eventInfo, true, null, "eventGateway", scaleFactor);
+
+ double r = width / 6.;
+
+ // create pentagon (coords with respect to center)
+ int topX = (int)(.95 * r); // top right corner
+ int topY = (int)(-.31 * r);
+ int bottomX = (int)(.59 * r); // bottom right corner
+ int bottomY = (int)(.81 * r);
+
+ int[] xPoints = new int[]{ 0, topX, bottomX, -bottomX, -topX };
+ int[] yPoints = new int[]{ -(int)r, topY, bottomY, bottomY, topY };
+ Polygon pentagon = new Polygon(xPoints, yPoints, 5);
+ pentagon.translate(x+width/2, y+width/2);
+
+ // draw
+ g.drawPolygon(pentagon);
+ }
+ }
+
+ public void drawMultiInstanceMarker(boolean sequential, int x, int y, int width, int height) {
+ int rectangleWidth = MARKER_WIDTH;
+ int rectangleHeight = MARKER_WIDTH;
+ int lineX = x + (width - rectangleWidth) / 2;
+ int lineY = y + height - rectangleHeight - 3;
+
+ Stroke orginalStroke = g.getStroke();
+ g.setStroke(MULTI_INSTANCE_STROKE);
+
+ if (sequential) {
+ g.draw(new Line2D.Double(lineX, lineY, lineX + rectangleWidth, lineY));
+ g.draw(new Line2D.Double(lineX, lineY + rectangleHeight / 2, lineX + rectangleWidth, lineY + rectangleHeight / 2));
+ g.draw(new Line2D.Double(lineX, lineY + rectangleHeight, lineX + rectangleWidth, lineY + rectangleHeight));
+ } else {
+ g.draw(new Line2D.Double(lineX, lineY, lineX, lineY + rectangleHeight));
+ g.draw(new Line2D.Double(lineX + rectangleWidth / 2, lineY, lineX + rectangleWidth / 2, lineY + rectangleHeight));
+ g.draw(new Line2D.Double(lineX + rectangleWidth, lineY, lineX + rectangleWidth, lineY + rectangleHeight));
+ }
+
+ g.setStroke(orginalStroke);
+ }
+
+ public void drawHighLight(int x, int y, int width, int height) {
+ Paint originalPaint = g.getPaint();
+ Stroke originalStroke = g.getStroke();
+
+ g.setPaint(HIGHLIGHT_COLOR);
+// g.setPaint(Color.GREEN);
+ g.setStroke(THICK_TASK_BORDER_STROKE);
+
+ RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
+ g.draw(rect);
+
+ g.setPaint(originalPaint);
+ g.setStroke(originalStroke);
+ }
+
+
+ public void drawHighLight(int x, int y, int width, int height,Color color) {
+ Paint originalPaint = g.getPaint();
+ Stroke originalStroke = g.getStroke();
+
+ g.setPaint(color);
+// g.setPaint(Color.GREEN);
+ g.setStroke(THICK_TASK_BORDER_STROKE);
+
+ RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
+ g.draw(rect);
+
+ g.setPaint(originalPaint);
+ g.setStroke(originalStroke);
+ }
+
+ public void drawTextAnnotation(String text, GraphicInfo graphicInfo) {
+ int x = (int) graphicInfo.getX();
+ int y = (int) graphicInfo.getY();
+ int width = (int) graphicInfo.getWidth();
+ int height = (int) graphicInfo.getHeight();
+
+ Font originalFont = g.getFont();
+ Stroke originalStroke = g.getStroke();
+
+ g.setFont(ANNOTATION_FONT);
+
+ Path2D path = new Path2D.Double();
+ x += .5;
+ int lineLength = 18;
+ path.moveTo(x + lineLength, y);
+ path.lineTo(x, y);
+ path.lineTo(x, y + height);
+ path.lineTo(x + lineLength, y + height);
+
+ path.lineTo(x + lineLength, y + height -1);
+ path.lineTo(x + 1, y + height -1);
+ path.lineTo(x + 1, y + 1);
+ path.lineTo(x + lineLength, y + 1);
+ path.closePath();
+
+ g.draw(path);
+
+ int boxWidth = width - (2 * ANNOTATION_TEXT_PADDING);
+ int boxHeight = height - (2 * ANNOTATION_TEXT_PADDING);
+ int boxX = x + width/2 - boxWidth/2;
+ int boxY = y + height/2 - boxHeight/2;
+
+ if (text != null && text.isEmpty() == false) {
+ drawMultilineAnnotationText(text, boxX, boxY, boxWidth, boxHeight);
+ }
+
+ // restore originals
+ g.setFont(originalFont);
+ g.setStroke(originalStroke);
+ }
+
+ public void drawLabel(String text, GraphicInfo graphicInfo){
+ drawLabel(text, graphicInfo, true);
+ }
+ public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered){
+ float interline = 1.0f;
+
+ // text
+ if (text != null && text.length()>0) {
+ Paint originalPaint = g.getPaint();
+ Font originalFont = g.getFont();
+
+ g.setPaint(LABEL_COLOR);
+ g.setFont(LABEL_FONT);
+
+ int wrapWidth = 100;
+ int textY = (int) graphicInfo.getY();
+
+ // TODO: use drawMultilineText()
+ AttributedString as = new AttributedString(text);
+ as.addAttribute(TextAttribute.FOREGROUND, g.getPaint());
+ as.addAttribute(TextAttribute.FONT, g.getFont());
+ AttributedCharacterIterator aci = as.getIterator();
+ FontRenderContext frc = new FontRenderContext(null, true, false);
+ LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
+
+ while (lbm.getPosition() < text.length()) {
+ TextLayout tl = lbm.nextLayout(wrapWidth);
+ textY += tl.getAscent();
+ Rectangle2D bb = tl.getBounds();
+ double tX = graphicInfo.getX();
+ if (centered) {
+ tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2);
+ }
+ tl.draw(g, (float) tX, textY);
+ textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent();
+ }
+
+ // restore originals
+ g.setFont(originalFont);
+ g.setPaint(originalPaint);
+ }
+ }
+
+ /**
+ * This method makes coordinates of connection flow better.
+ * @param sourceShapeType
+ * @param targetShapeType
+ * @param sourceGraphicInfo
+ * @param targetGraphicInfo
+ * @param graphicInfoList
+ *
+ */
+ public List connectionPerfectionizer(SHAPE_TYPE sourceShapeType, SHAPE_TYPE targetShapeType, GraphicInfo sourceGraphicInfo, GraphicInfo targetGraphicInfo, List graphicInfoList) {
+ Shape shapeFirst = createShape(sourceShapeType, sourceGraphicInfo);
+ Shape shapeLast = createShape(targetShapeType, targetGraphicInfo);
+
+ if (graphicInfoList != null && graphicInfoList.size() > 0) {
+ GraphicInfo graphicInfoFirst = graphicInfoList.get(0);
+ GraphicInfo graphicInfoLast = graphicInfoList.get(graphicInfoList.size()-1);
+ if (shapeFirst != null) {
+ graphicInfoFirst.setX(shapeFirst.getBounds2D().getCenterX());
+ graphicInfoFirst.setY(shapeFirst.getBounds2D().getCenterY());
+ }
+ if (shapeLast != null) {
+ graphicInfoLast.setX(shapeLast.getBounds2D().getCenterX());
+ graphicInfoLast.setY(shapeLast.getBounds2D().getCenterY());
+ }
+
+ Point p = null;
+
+ if (shapeFirst != null) {
+ Line2D.Double lineFirst = new Line2D.Double(graphicInfoFirst.getX(), graphicInfoFirst.getY(), graphicInfoList.get(1).getX(), graphicInfoList.get(1).getY());
+ p = getIntersection(shapeFirst, lineFirst);
+ if (p != null) {
+ graphicInfoFirst.setX(p.getX());
+ graphicInfoFirst.setY(p.getY());
+ }
+ }
+
+ if (shapeLast != null) {
+ Line2D.Double lineLast = new Line2D.Double(graphicInfoLast.getX(), graphicInfoLast.getY(), graphicInfoList.get(graphicInfoList.size()-2).getX(), graphicInfoList.get(graphicInfoList.size()-2).getY());
+ p = getIntersection(shapeLast, lineLast);
+ if (p != null) {
+ graphicInfoLast.setX(p.getX());
+ graphicInfoLast.setY(p.getY());
+ }
+ }
+ }
+
+ return graphicInfoList;
+ }
+
+ /**
+ * This method creates shape by type and coordinates.
+ * @param shapeType
+ * @param graphicInfo
+ * @return Shape
+ */
+ private static Shape createShape(SHAPE_TYPE shapeType, GraphicInfo graphicInfo) {
+ if (SHAPE_TYPE.Rectangle.equals(shapeType)) {
+ // source is rectangle
+ return new Rectangle2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight());
+ } else if (SHAPE_TYPE.Rhombus.equals(shapeType)) {
+ // source is rhombus
+ Path2D.Double rhombus = new Path2D.Double();
+ rhombus.moveTo(graphicInfo.getX(), graphicInfo.getY() + graphicInfo.getHeight() / 2);
+ rhombus.lineTo(graphicInfo.getX() + graphicInfo.getWidth() / 2, graphicInfo.getY() + graphicInfo.getHeight());
+ rhombus.lineTo(graphicInfo.getX() + graphicInfo.getWidth(), graphicInfo.getY() + graphicInfo.getHeight() / 2);
+ rhombus.lineTo(graphicInfo.getX() + graphicInfo.getWidth() / 2, graphicInfo.getY());
+ rhombus.lineTo(graphicInfo.getX(), graphicInfo.getY() + graphicInfo.getHeight() / 2);
+ rhombus.closePath();
+ return rhombus;
+ } else if (SHAPE_TYPE.Ellipse.equals(shapeType)) {
+ // source is ellipse
+ return new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), graphicInfo.getWidth(), graphicInfo.getHeight());
+ } else {
+ // unknown source element, just do not correct coordinates
+ }
+ return null;
+ }
+
+ /**
+ * This method returns intersection point of shape border and line.
+ *
+ * @param shape
+ * @param line
+ * @return Point
+ */
+ private static Point getIntersection(Shape shape, Line2D.Double line) {
+ if (shape instanceof Ellipse2D) {
+ return getEllipseIntersection(shape, line);
+ } else if (shape instanceof Rectangle2D || shape instanceof Path2D) {
+ return getShapeIntersection(shape, line);
+ } else {
+ // something strange
+ return null;
+ }
+ }
+
+ /**
+ * This method calculates ellipse intersection with line
+ * @param shape
+ * Bounds of this shape used to calculate parameters of inscribed into this bounds ellipse.
+ * @param line
+ * @return Intersection point
+ */
+ private static Point getEllipseIntersection(Shape shape, Line2D.Double line) {
+ double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
+ double x = shape.getBounds2D().getWidth()/2 * Math.cos(angle) + shape.getBounds2D().getCenterX();
+ double y = shape.getBounds2D().getHeight()/2 * Math.sin(angle) + shape.getBounds2D().getCenterY();
+ Point p = new Point();
+ p.setLocation(x, y);
+ return p;
+ }
+
+ /**
+ * This method calculates shape intersection with line.
+ *
+ * @param shape
+ * @param line
+ * @return Intersection point
+ */
+ private static Point getShapeIntersection(Shape shape, Line2D.Double line) {
+ PathIterator it = shape.getPathIterator(null);
+ double[] coords = new double[6];
+ double[] pos = new double[2];
+ Line2D.Double l = new Line2D.Double();
+ while (!it.isDone()) {
+ int type = it.currentSegment(coords);
+ switch (type) {
+ case PathIterator.SEG_MOVETO:
+ pos[0] = coords[0];
+ pos[1] = coords[1];
+ break;
+ case PathIterator.SEG_LINETO:
+ l = new Line2D.Double(pos[0], pos[1], coords[0], coords[1]);
+ if (line.intersectsLine(l)) {
+ return getLinesIntersection(line, l);
+ }
+ pos[0] = coords[0];
+ pos[1] = coords[1];
+ break;
+ case PathIterator.SEG_CLOSE:
+ break;
+ default:
+ // whatever
+ }
+ it.next();
+ }
+ return null;
+ }
+
+ /**
+ * This method calculates intersections of two lines.
+ * @param a Line 1
+ * @param b Line 2
+ * @return Intersection point
+ */
+ private static Point getLinesIntersection(Line2D a, Line2D b) {
+ double d = (a.getX1()-a.getX2())*(b.getY2()-b.getY1()) - (a.getY1()-a.getY2())*(b.getX2()-b.getX1());
+ double da = (a.getX1()-b.getX1())*(b.getY2()-b.getY1()) - (a.getY1()-b.getY1())*(b.getX2()-b.getX1());
+ // double db = (a.getX1()-a.getX2())*(a.getY1()-b.getY1()) - (a.getY1()-a.getY2())*(a.getX1()-b.getX1());
+ double ta = da/d;
+ // double tb = db/d;
+ Point p = new Point();
+ p.setLocation(a.getX1()+ta*(a.getX2()-a.getX1()), a.getY1()+ta*(a.getY2()-a.getY1()));
+ return p;
+ }
+}
diff --git a/len-activiti/src/main/java/org/activiti/image/impl/DefaultProcessDiagramGenerator.java b/len-activiti/src/main/java/org/activiti/image/impl/DefaultProcessDiagramGenerator.java
new file mode 100644
index 0000000..c4a72ab
--- /dev/null
+++ b/len-activiti/src/main/java/org/activiti/image/impl/DefaultProcessDiagramGenerator.java
@@ -0,0 +1,1164 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.activiti.image.impl;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.activiti.bpmn.model.Activity;
+import org.activiti.bpmn.model.Artifact;
+import org.activiti.bpmn.model.Association;
+import org.activiti.bpmn.model.AssociationDirection;
+import org.activiti.bpmn.model.BaseElement;
+import org.activiti.bpmn.model.BoundaryEvent;
+import org.activiti.bpmn.model.BpmnModel;
+import org.activiti.bpmn.model.BusinessRuleTask;
+import org.activiti.bpmn.model.CallActivity;
+import org.activiti.bpmn.model.CompensateEventDefinition;
+import org.activiti.bpmn.model.EndEvent;
+import org.activiti.bpmn.model.ErrorEventDefinition;
+import org.activiti.bpmn.model.Event;
+import org.activiti.bpmn.model.EventDefinition;
+import org.activiti.bpmn.model.EventGateway;
+import org.activiti.bpmn.model.EventSubProcess;
+import org.activiti.bpmn.model.ExclusiveGateway;
+import org.activiti.bpmn.model.FlowElement;
+import org.activiti.bpmn.model.FlowElementsContainer;
+import org.activiti.bpmn.model.FlowNode;
+import org.activiti.bpmn.model.Gateway;
+import org.activiti.bpmn.model.GraphicInfo;
+import org.activiti.bpmn.model.InclusiveGateway;
+import org.activiti.bpmn.model.IntermediateCatchEvent;
+import org.activiti.bpmn.model.Lane;
+import org.activiti.bpmn.model.ManualTask;
+import org.activiti.bpmn.model.MessageEventDefinition;
+import org.activiti.bpmn.model.MultiInstanceLoopCharacteristics;
+import org.activiti.bpmn.model.ParallelGateway;
+import org.activiti.bpmn.model.Pool;
+import org.activiti.bpmn.model.Process;
+import org.activiti.bpmn.model.ReceiveTask;
+import org.activiti.bpmn.model.ScriptTask;
+import org.activiti.bpmn.model.SendTask;
+import org.activiti.bpmn.model.SequenceFlow;
+import org.activiti.bpmn.model.ServiceTask;
+import org.activiti.bpmn.model.SignalEventDefinition;
+import org.activiti.bpmn.model.StartEvent;
+import org.activiti.bpmn.model.SubProcess;
+import org.activiti.bpmn.model.Task;
+import org.activiti.bpmn.model.TextAnnotation;
+import org.activiti.bpmn.model.ThrowEvent;
+import org.activiti.bpmn.model.TimerEventDefinition;
+import org.activiti.bpmn.model.UserTask;
+import org.activiti.image.HMProcessDiagramGenerator;
+import org.activiti.image.ProcessDiagramGenerator;
+
+/**
+ * Class to generate an image based the diagram interchange information in a
+ * BPMN 2.0 process.
+ *
+ * @author Joram Barrez
+ * @author Tijs Rademakers
+ */
+public class DefaultProcessDiagramGenerator implements HMProcessDiagramGenerator {
+
+ protected Map, ActivityDrawInstruction> activityDrawInstructions = new HashMap, ActivityDrawInstruction>();
+ protected Map, ArtifactDrawInstruction> artifactDrawInstructions = new HashMap, ArtifactDrawInstruction>();
+
+ public DefaultProcessDiagramGenerator() {
+ this(1.0);
+ }
+
+ // The instructions on how to draw a certain construct is
+ // created statically and stored in a map for performance.
+ public DefaultProcessDiagramGenerator(final double scaleFactor) {
+ // start event
+ activityDrawInstructions.put(StartEvent.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ StartEvent startEvent = (StartEvent) flowNode;
+ if (startEvent.getEventDefinitions() != null && !startEvent.getEventDefinitions().isEmpty()) {
+ EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);
+ if (eventDefinition instanceof TimerEventDefinition) {
+ processDiagramCanvas.drawTimerStartEvent(graphicInfo, scaleFactor);
+ } else if (eventDefinition instanceof ErrorEventDefinition) {
+ processDiagramCanvas.drawErrorStartEvent(graphicInfo, scaleFactor);
+ } else if (eventDefinition instanceof SignalEventDefinition) {
+ processDiagramCanvas.drawSignalStartEvent(graphicInfo, scaleFactor);
+ } else if (eventDefinition instanceof MessageEventDefinition) {
+ processDiagramCanvas.drawMessageStartEvent(graphicInfo, scaleFactor);
+ } else {
+ processDiagramCanvas.drawNoneStartEvent(graphicInfo);
+ }
+ } else {
+ processDiagramCanvas.drawNoneStartEvent(graphicInfo);
+ }
+ }
+ });
+
+ // signal catch
+ activityDrawInstructions.put(IntermediateCatchEvent.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) flowNode;
+ if (intermediateCatchEvent.getEventDefinitions() != null && !intermediateCatchEvent.getEventDefinitions()
+ .isEmpty()) {
+ if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
+ processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
+ } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) {
+ processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
+ } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) {
+ processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
+ }
+ }
+ }
+ });
+
+ // signal throw
+ activityDrawInstructions.put(ThrowEvent.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ ThrowEvent throwEvent = (ThrowEvent) flowNode;
+ if (throwEvent.getEventDefinitions() != null && !throwEvent.getEventDefinitions().isEmpty()) {
+ if (throwEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
+ processDiagramCanvas.drawThrowingSignalEvent(graphicInfo, scaleFactor);
+ } else if (throwEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) {
+ processDiagramCanvas.drawThrowingCompensateEvent(graphicInfo, scaleFactor);
+ } else {
+ processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
+ }
+ } else {
+ processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
+ }
+ }
+ });
+
+ // end event
+ activityDrawInstructions.put(EndEvent.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ EndEvent endEvent = (EndEvent) flowNode;
+ if (endEvent.getEventDefinitions() != null && !endEvent.getEventDefinitions().isEmpty()) {
+ if (endEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {
+ processDiagramCanvas.drawErrorEndEvent(flowNode.getName(), graphicInfo, scaleFactor);
+ } else {
+ processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
+ }
+ } else {
+ processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
+ }
+ }
+ });
+
+ // task
+ activityDrawInstructions.put(Task.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawTask(flowNode.getName(), graphicInfo);
+ }
+ });
+
+ // user task
+ activityDrawInstructions.put(UserTask.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawUserTask(flowNode.getName(), graphicInfo, scaleFactor);
+ }
+ });
+
+ // script task
+ activityDrawInstructions.put(ScriptTask.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawScriptTask(flowNode.getName(), graphicInfo, scaleFactor);
+ }
+ });
+
+ // service task
+ activityDrawInstructions.put(ServiceTask.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ ServiceTask serviceTask = (ServiceTask) flowNode;
+ if ("camel".equalsIgnoreCase(serviceTask.getType())) {
+ processDiagramCanvas.drawCamelTask(serviceTask.getName(), graphicInfo, scaleFactor);
+ } else if ("mule".equalsIgnoreCase(serviceTask.getType())) {
+ processDiagramCanvas.drawMuleTask(serviceTask.getName(), graphicInfo, scaleFactor);
+ } else {
+ processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);
+ }
+ }
+ });
+
+ // receive task
+ activityDrawInstructions.put(ReceiveTask.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawReceiveTask(flowNode.getName(), graphicInfo, scaleFactor);
+ }
+ });
+
+ // send task
+ activityDrawInstructions.put(SendTask.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawSendTask(flowNode.getName(), graphicInfo, scaleFactor);
+ }
+ });
+
+ // manual task
+ activityDrawInstructions.put(ManualTask.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawManualTask(flowNode.getName(), graphicInfo, scaleFactor);
+ }
+ });
+
+ // businessRuleTask task
+ activityDrawInstructions.put(BusinessRuleTask.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawBusinessRuleTask(flowNode.getName(), graphicInfo, scaleFactor);
+ }
+ });
+
+ // exclusive gateway
+ activityDrawInstructions.put(ExclusiveGateway.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawExclusiveGateway(graphicInfo, scaleFactor);
+ }
+ });
+
+ // inclusive gateway
+ activityDrawInstructions.put(InclusiveGateway.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawInclusiveGateway(graphicInfo, scaleFactor);
+ }
+ });
+
+ // parallel gateway
+ activityDrawInstructions.put(ParallelGateway.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawParallelGateway(graphicInfo, scaleFactor);
+ }
+ });
+
+ // event based gateway
+ activityDrawInstructions.put(EventGateway.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawEventBasedGateway(graphicInfo, scaleFactor);
+ }
+ });
+
+
+ // Boundary timer
+ activityDrawInstructions.put(BoundaryEvent.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ BoundaryEvent boundaryEvent = (BoundaryEvent) flowNode;
+ if (boundaryEvent.getEventDefinitions() != null && !boundaryEvent.getEventDefinitions().isEmpty()) {
+ if (boundaryEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) {
+
+ processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+ } else if (boundaryEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {
+
+ processDiagramCanvas.drawCatchingErrorEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+ } else if (boundaryEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
+ processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+ } else if (boundaryEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) {
+ processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+
+ } else if (boundaryEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) {
+ processDiagramCanvas.drawCatchingCompensateEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
+ }
+ }
+
+ }
+ });
+
+ // subprocess
+ activityDrawInstructions.put(SubProcess.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+ processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false);
+ } else {
+ processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
+ }
+ }
+ });
+
+ // Event subprocess
+ activityDrawInstructions.put(EventSubProcess.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
+ processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, true);
+ } else {
+ processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);
+ }
+ }
+ });
+
+ // call activity
+ activityDrawInstructions.put(CallActivity.class, new ActivityDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ processDiagramCanvas.drawCollapsedCallActivity(flowNode.getName(), graphicInfo);
+ }
+ });
+
+ // text annotation
+ artifactDrawInstructions.put(TextAnnotation.class, new ArtifactDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
+ TextAnnotation textAnnotation = (TextAnnotation) artifact;
+ processDiagramCanvas.drawTextAnnotation(textAnnotation.getText(), graphicInfo);
+ }
+ });
+
+ // association
+ artifactDrawInstructions.put(Association.class, new ArtifactDrawInstruction() {
+
+ public void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
+ Association association = (Association) artifact;
+ String sourceRef = association.getSourceRef();
+ String targetRef = association.getTargetRef();
+
+ // source and target can be instance of FlowElement or Artifact
+ BaseElement sourceElement = bpmnModel.getFlowElement(sourceRef);
+ BaseElement targetElement = bpmnModel.getFlowElement(targetRef);
+ if (sourceElement == null) {
+ sourceElement = bpmnModel.getArtifact(sourceRef);
+ }
+ if (targetElement == null) {
+ targetElement = bpmnModel.getArtifact(targetRef);
+ }
+ List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
+ graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
+ int xPoints[]= new int[graphicInfoList.size()];
+ int yPoints[]= new int[graphicInfoList.size()];
+ for (int i=1; i highLightedActivities, List highLightedFlows,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {
+
+ return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
+ activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor).generateImage(imageType);
+ }
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor,
+ List highLightedCurrentActivities) {
+
+ return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
+ activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor,highLightedCurrentActivities).generateImage(imageType);
+ }
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows) {
+ return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, 1.0);
+ }
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType,
+ List highLightedActivities, List highLightedFlows, double scaleFactor) {
+ return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor);
+ }
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities) {
+ return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList());
+ }
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, double scaleFactor) {
+ return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), scaleFactor);
+ }
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
+ return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
+ activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0);
+ }
+
+ public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
+ String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {
+
+ return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
+ activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor);
+ }
+
+ public InputStream generatePngDiagram(BpmnModel bpmnModel) {
+ return generatePngDiagram(bpmnModel, 1.0);
+ }
+
+ public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor) {
+ return generateDiagram(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor);
+ }
+
+ public InputStream generateJpgDiagram(BpmnModel bpmnModel) {
+ return generateJpgDiagram(bpmnModel, 1.0);
+ }
+
+ public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor) {
+ return generateDiagram(bpmnModel, "jpg", Collections.emptyList(), Collections.emptyList());
+ }
+
+ public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {
+
+ return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
+ activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor).generateBufferedImage(imageType);
+ }
+
+ public BufferedImage generateImage(BpmnModel bpmnModel, String imageType,
+ List highLightedActivities, List highLightedFlows, double scaleFactor) {
+
+ return generateImage(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor);
+ }
+
+ public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) {
+ return generateImage(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor);
+ }
+
+ protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
+ List highLightedActivities, List highLightedFlows,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {
+
+ prepareBpmnModel(bpmnModel);
+
+ DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
+
+ // Draw pool shape, if process is participant in collaboration
+ for (Pool pool : bpmnModel.getPools()) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
+ processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo);
+ }
+
+ // Draw lanes
+ for (Process process : bpmnModel.getProcesses()) {
+ for (Lane lane : process.getLanes()) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
+ processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo);
+ }
+ }
+
+ // Draw activities and their sequence-flows
+ for (Process process: bpmnModel.getProcesses()) {
+ for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) {
+ drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor);
+ }
+ }
+
+ // Draw artifacts
+ for (Process process : bpmnModel.getProcesses()) {
+
+ for (Artifact artifact : process.getArtifacts()) {
+ drawArtifact(processDiagramCanvas, bpmnModel, artifact);
+ }
+
+ List subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
+ if (subProcesses != null) {
+ for (SubProcess subProcess : subProcesses) {
+ for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
+ drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
+ }
+ }
+ }
+ }
+
+ return processDiagramCanvas;
+ }
+
+
+ protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
+ List highLightedActivities, List highLightedFlows,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor,
+ List highCurrentLightedActivities) {
+
+ prepareBpmnModel(bpmnModel);
+
+ DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
+
+ // Draw pool shape, if process is participant in collaboration
+ for (Pool pool : bpmnModel.getPools()) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
+ processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo);
+ }
+
+ // Draw lanes
+ for (Process process : bpmnModel.getProcesses()) {
+ for (Lane lane : process.getLanes()) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
+ processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo);
+ }
+ }
+
+ // Draw activities and their sequence-flows
+ for (Process process: bpmnModel.getProcesses()) {
+ for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) {
+ drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor,highCurrentLightedActivities);
+ }
+ }
+
+ // Draw artifacts
+ for (Process process : bpmnModel.getProcesses()) {
+
+ for (Artifact artifact : process.getArtifacts()) {
+ drawArtifact(processDiagramCanvas, bpmnModel, artifact);
+ }
+
+ List subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
+ if (subProcesses != null) {
+ for (SubProcess subProcess : subProcesses) {
+ for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
+ drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
+ }
+ }
+ }
+ }
+
+ return processDiagramCanvas;
+ }
+
+ protected void prepareBpmnModel(BpmnModel bpmnModel) {
+
+ // Need to make sure all elements have positive x and y.
+ // Check all graphicInfo and update the elements accordingly
+
+ List allGraphicInfos = new ArrayList();
+ if (bpmnModel.getLocationMap() != null) {
+ allGraphicInfos.addAll(bpmnModel.getLocationMap().values());
+ }
+ if (bpmnModel.getLabelLocationMap() != null) {
+ allGraphicInfos.addAll(bpmnModel.getLabelLocationMap().values());
+ }
+ if (bpmnModel.getFlowLocationMap() != null) {
+ for (List flowGraphicInfos : bpmnModel.getFlowLocationMap().values()) {
+ allGraphicInfos.addAll(flowGraphicInfos);
+ }
+ }
+
+ if (allGraphicInfos.size() > 0) {
+
+ boolean needsTranslationX = false;
+ boolean needsTranslationY = false;
+
+ double lowestX = 0.0;
+ double lowestY = 0.0;
+
+ // Collect lowest x and y
+ for (GraphicInfo graphicInfo : allGraphicInfos) {
+
+ double x = graphicInfo.getX();
+ double y = graphicInfo.getY();
+
+ if (x < lowestX) {
+ needsTranslationX = true;
+ lowestX = x;
+ }
+ if (y < lowestY) {
+ needsTranslationY = true;
+ lowestY = y;
+ }
+
+ }
+
+ // Update all graphicInfo objects
+ if (needsTranslationX || needsTranslationY) {
+
+ double translationX = Math.abs(lowestX);
+ double translationY = Math.abs(lowestY);
+
+ for (GraphicInfo graphicInfo : allGraphicInfos) {
+ if (needsTranslationX) {
+ graphicInfo.setX(graphicInfo.getX() + translationX);
+ }
+ if(needsTranslationY) {
+ graphicInfo.setY(graphicInfo.getY() + translationY);
+ }
+ }
+ }
+
+ }
+
+ }
+
+ protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
+ FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor) {
+
+ ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
+ if (drawInstruction != null) {
+
+ drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
+
+ // Gather info on the multi instance marker
+ boolean multiInstanceSequential = false, multiInstanceParallel = false, collapsed = false;
+ if (flowNode instanceof Activity) {
+ Activity activity = (Activity) flowNode;
+ MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
+ if (multiInstanceLoopCharacteristics != null) {
+ multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
+ multiInstanceParallel = !multiInstanceSequential;
+ }
+ }
+
+ // Gather info on the collapsed marker
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ if (flowNode instanceof SubProcess) {
+ collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
+ } else if (flowNode instanceof CallActivity) {
+ collapsed = true;
+ }
+
+ if (scaleFactor == 1.0) {
+ // Actually draw the markers
+ processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(),(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
+ multiInstanceSequential, multiInstanceParallel, collapsed);
+ }
+
+ // Draw highlighted activities
+ if (highLightedActivities.contains(flowNode.getId())) {
+ drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
+ }
+
+ }
+
+ // Outgoing transitions of activity
+ for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
+ boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
+ String defaultFlow = null;
+ if (flowNode instanceof Activity) {
+ defaultFlow = ((Activity) flowNode).getDefaultFlow();
+ } else if (flowNode instanceof Gateway) {
+ defaultFlow = ((Gateway) flowNode).getDefaultFlow();
+ }
+
+ boolean isDefault = false;
+ if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {
+ isDefault = true;
+ }
+ boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway);
+
+ String sourceRef = sequenceFlow.getSourceRef();
+ String targetRef = sequenceFlow.getTargetRef();
+ FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
+ FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
+ List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
+ if (graphicInfoList != null && graphicInfoList.size() > 0) {
+ graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
+ int xPoints[]= new int[graphicInfoList.size()];
+ int yPoints[]= new int[graphicInfoList.size()];
+
+ for (int i=1; i highLightedActivities, List highLightedFlows, double scaleFactor, List highLightedExecutedActivities) {
+
+ ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
+ if (drawInstruction != null) {
+
+ drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
+
+ // Gather info on the multi instance marker
+ boolean multiInstanceSequential = false, multiInstanceParallel = false, collapsed = false;
+ if (flowNode instanceof Activity) {
+ Activity activity = (Activity) flowNode;
+ MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
+ if (multiInstanceLoopCharacteristics != null) {
+ multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
+ multiInstanceParallel = !multiInstanceSequential;
+ }
+ }
+
+ // Gather info on the collapsed marker
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+ if (flowNode instanceof SubProcess) {
+ collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
+ } else if (flowNode instanceof CallActivity) {
+ collapsed = true;
+ }
+
+ if (scaleFactor == 1.0) {
+ // Actually draw the markers
+ processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(),(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
+ multiInstanceSequential, multiInstanceParallel, collapsed);
+ }
+
+ // Draw highlighted activities
+ if (highLightedActivities.contains(flowNode.getId())) {
+ drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
+ }
+
+ if (highLightedExecutedActivities.contains(flowNode.getId())) {
+ processDiagramCanvas.HIGHLIGHT_COLOR = Color.RED;
+ drawColorHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()),Color.RED);
+ processDiagramCanvas.HIGHLIGHT_COLOR = Color.GREEN;
+ }
+
+ }
+
+ // Outgoing transitions of activity
+ for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
+ boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
+ String defaultFlow = null;
+ if (flowNode instanceof Activity) {
+ defaultFlow = ((Activity) flowNode).getDefaultFlow();
+ } else if (flowNode instanceof Gateway) {
+ defaultFlow = ((Gateway) flowNode).getDefaultFlow();
+ }
+
+ boolean isDefault = false;
+ if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {
+ isDefault = true;
+ }
+ boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway);
+
+ String sourceRef = sequenceFlow.getSourceRef();
+ String targetRef = sequenceFlow.getTargetRef();
+ FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
+ FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
+ List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
+ if (graphicInfoList != null && graphicInfoList.size() > 0) {
+ graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
+ int xPoints[]= new int[graphicInfoList.size()];
+ int yPoints[]= new int[graphicInfoList.size()];
+
+ for (int i=1; i connectionPerfectionizer(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, BaseElement sourceElement, BaseElement targetElement, List graphicInfoList) {
+ GraphicInfo sourceGraphicInfo = bpmnModel.getGraphicInfo(sourceElement.getId());
+ GraphicInfo targetGraphicInfo = bpmnModel.getGraphicInfo(targetElement.getId());
+
+ DefaultProcessDiagramCanvas.SHAPE_TYPE sourceShapeType = getShapeType(sourceElement);
+ DefaultProcessDiagramCanvas.SHAPE_TYPE targetShapeType = getShapeType(targetElement);
+
+ return processDiagramCanvas.connectionPerfectionizer(sourceShapeType, targetShapeType, sourceGraphicInfo, targetGraphicInfo, graphicInfoList);
+ }
+
+ /**
+ * This method returns shape type of base element.
+ * Each element can be presented as rectangle, rhombus, or ellipse.
+ * @param baseElement
+ * @return DefaultProcessDiagramCanvas.SHAPE_TYPE
+ */
+ protected static DefaultProcessDiagramCanvas.SHAPE_TYPE getShapeType(BaseElement baseElement) {
+ if (baseElement instanceof Task || baseElement instanceof Activity || baseElement instanceof TextAnnotation) {
+ return DefaultProcessDiagramCanvas.SHAPE_TYPE.Rectangle;
+ } else if (baseElement instanceof Gateway) {
+ return DefaultProcessDiagramCanvas.SHAPE_TYPE.Rhombus;
+ } else if (baseElement instanceof Event) {
+ return DefaultProcessDiagramCanvas.SHAPE_TYPE.Ellipse;
+ } else {
+ // unknown source element, just do not correct coordinates
+ }
+ return null;
+ }
+
+ protected static GraphicInfo getLineCenter(List graphicInfoList) {
+ GraphicInfo gi = new GraphicInfo();
+
+ int xPoints[]= new int[graphicInfoList.size()];
+ int yPoints[]= new int[graphicInfoList.size()];
+
+ double length = 0;
+ double[] lengths = new double[graphicInfoList.size()];
+ lengths[0] = 0;
+ double m;
+ for (int i=1; i m) {
+ break;
+ }
+ }
+
+ GraphicInfo graphicInfo1 = graphicInfoList.get(p1);
+ GraphicInfo graphicInfo2 = graphicInfoList.get(p2);
+
+ double AB = (int)graphicInfo2.getX() - (int)graphicInfo1.getX();
+ double OA = (int)graphicInfo2.getY() - (int)graphicInfo1.getY();
+ double OB = lengths[p2] - lengths[p1];
+ double ob = m - lengths[p1];
+ double ab = AB * ob / OB;
+ double oa = OA * ob / OB;
+
+ double mx = graphicInfo1.getX() + ab;
+ double my = graphicInfo1.getY() + oa;
+
+ gi.setX(mx);
+ gi.setY(my);
+ return gi;
+ }
+
+ protected void drawArtifact(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
+ ArtifactDrawInstruction drawInstruction = artifactDrawInstructions.get(artifact.getClass());
+ if (drawInstruction != null) {
+ drawInstruction.draw(processDiagramCanvas, bpmnModel, artifact);
+ }
+ }
+
+ private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
+ processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
+
+ }
+
+
+ private static void drawColorHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo,Color color) {
+ processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),color);
+
+ }
+
+ protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,
+ String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
+
+ // We need to calculate maximum values to know how big the image will be in its entirety
+ double minX = Double.MAX_VALUE;
+ double maxX = 0;
+ double minY = Double.MAX_VALUE;
+ double maxY = 0;
+
+ for (Pool pool : bpmnModel.getPools()) {
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
+ minX = graphicInfo.getX();
+ maxX = graphicInfo.getX() + graphicInfo.getWidth();
+ minY = graphicInfo.getY();
+ maxY = graphicInfo.getY() + graphicInfo.getHeight();
+ }
+
+ List flowNodes = gatherAllFlowNodes(bpmnModel);
+ for (FlowNode flowNode : flowNodes) {
+
+ GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
+
+ // width
+ if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
+ maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
+ }
+ if (flowNodeGraphicInfo.getX() < minX) {
+ minX = flowNodeGraphicInfo.getX();
+ }
+ // height
+ if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
+ maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
+ }
+ if (flowNodeGraphicInfo.getY() < minY) {
+ minY = flowNodeGraphicInfo.getY();
+ }
+
+ for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
+ List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
+ if (graphicInfoList != null) {
+ for (GraphicInfo graphicInfo : graphicInfoList) {
+ // width
+ if (graphicInfo.getX() > maxX) {
+ maxX = graphicInfo.getX();
+ }
+ if (graphicInfo.getX() < minX) {
+ minX = graphicInfo.getX();
+ }
+ // height
+ if (graphicInfo.getY() > maxY) {
+ maxY = graphicInfo.getY();
+ }
+ if (graphicInfo.getY()< minY) {
+ minY = graphicInfo.getY();
+ }
+ }
+ }
+ }
+ }
+
+ List artifacts = gatherAllArtifacts(bpmnModel);
+ for (Artifact artifact : artifacts) {
+
+ GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
+
+ if (artifactGraphicInfo != null) {
+ // width
+ if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) {
+ maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();
+ }
+ if (artifactGraphicInfo.getX() < minX) {
+ minX = artifactGraphicInfo.getX();
+ }
+ // height
+ if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {
+ maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();
+ }
+ if (artifactGraphicInfo.getY() < minY) {
+ minY = artifactGraphicInfo.getY();
+ }
+ }
+
+ List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
+ if (graphicInfoList != null) {
+ for (GraphicInfo graphicInfo : graphicInfoList) {
+ // width
+ if (graphicInfo.getX() > maxX) {
+ maxX = graphicInfo.getX();
+ }
+ if (graphicInfo.getX() < minX) {
+ minX = graphicInfo.getX();
+ }
+ // height
+ if (graphicInfo.getY() > maxY) {
+ maxY = graphicInfo.getY();
+ }
+ if (graphicInfo.getY()< minY) {
+ minY = graphicInfo.getY();
+ }
+ }
+ }
+ }
+
+ int nrOfLanes = 0;
+ for (Process process : bpmnModel.getProcesses()) {
+ for (Lane l : process.getLanes()) {
+
+ nrOfLanes++;
+
+ GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
+ // // width
+ if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
+ maxX = graphicInfo.getX() + graphicInfo.getWidth();
+ }
+ if (graphicInfo.getX() < minX) {
+ minX = graphicInfo.getX();
+ }
+ // height
+ if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
+ maxY = graphicInfo.getY() + graphicInfo.getHeight();
+ }
+ if (graphicInfo.getY() < minY) {
+ minY = graphicInfo.getY();
+ }
+ }
+ }
+
+ // Special case, see https://activiti.atlassian.net/browse/ACT-1431
+ if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {
+ // Nothing to show
+ minX = 0;
+ minY = 0;
+ }
+
+ return new DefaultProcessDiagramCanvas((int) maxX + 10,(int) maxY + 10, (int) minX, (int) minY,
+ imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
+ }
+
+
+ protected static List gatherAllArtifacts(BpmnModel bpmnModel) {
+ List artifacts = new ArrayList();
+ for (Process process : bpmnModel.getProcesses()) {
+ artifacts.addAll(process.getArtifacts());
+ }
+ return artifacts;
+ }
+
+ protected static List gatherAllFlowNodes(BpmnModel bpmnModel) {
+ List flowNodes = new ArrayList();
+ for (Process process : bpmnModel.getProcesses()) {
+ flowNodes.addAll(gatherAllFlowNodes(process));
+ }
+ return flowNodes;
+ }
+
+ protected static List gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) {
+ List flowNodes = new ArrayList();
+ for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
+ if (flowElement instanceof FlowNode) {
+ flowNodes.add((FlowNode) flowElement);
+ }
+ if (flowElement instanceof FlowElementsContainer) {
+ flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
+ }
+ }
+ return flowNodes;
+ }
+
+ public Map, ActivityDrawInstruction> getActivityDrawInstructions() {
+ return activityDrawInstructions;
+ }
+
+ public void setActivityDrawInstructions(
+ Map, ActivityDrawInstruction> activityDrawInstructions) {
+ this.activityDrawInstructions = activityDrawInstructions;
+ }
+
+ public Map, ArtifactDrawInstruction> getArtifactDrawInstructions() {
+ return artifactDrawInstructions;
+ }
+
+ public void setArtifactDrawInstructions(
+ Map, ArtifactDrawInstruction> artifactDrawInstructions) {
+ this.artifactDrawInstructions = artifactDrawInstructions;
+ }
+
+
+
+ protected interface ActivityDrawInstruction {
+ void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode);
+ }
+
+ protected interface ArtifactDrawInstruction {
+ void draw(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact);
+ }
+}
diff --git a/len-activiti/src/main/java/org/activiti/rest/editor/main/StencilsetRestResource.java b/len-activiti/src/main/java/org/activiti/rest/editor/main/StencilsetRestResource.java
index 734fc9b..80cd9d4 100644
--- a/len-activiti/src/main/java/org/activiti/rest/editor/main/StencilsetRestResource.java
+++ b/len-activiti/src/main/java/org/activiti/rest/editor/main/StencilsetRestResource.java
@@ -24,7 +24,7 @@ import org.springframework.web.bind.annotation.RestController;
* @author Tijs Rademakers
*/
@RestController
-@RequestMapping("service")
+@RequestMapping("/service")
public class StencilsetRestResource {
@RequestMapping(value="/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
diff --git a/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelEditorJsonRestResource.java b/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelEditorJsonRestResource.java
index c0a6671..98648b5 100644
--- a/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelEditorJsonRestResource.java
+++ b/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelEditorJsonRestResource.java
@@ -32,7 +32,7 @@ import org.springframework.web.bind.annotation.RestController;
* @author Tijs Rademakers
*/
@RestController
-@RequestMapping("service")
+@RequestMapping("/service")
@Slf4j
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {
diff --git a/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelSaveRestResource.java b/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelSaveRestResource.java
index 034418f..9d71be3 100644
--- a/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelSaveRestResource.java
+++ b/len-activiti/src/main/java/org/activiti/rest/editor/model/ModelSaveRestResource.java
@@ -45,7 +45,7 @@ import com.alibaba.fastjson.JSONObject;
* @author Tijs Rademakers
*/
@RestController
-@RequestMapping("service")
+@RequestMapping("/service")
@Slf4j
public class ModelSaveRestResource implements ModelDataJsonConstants {
diff --git a/len-activiti/src/main/resources/ftl/act/declare/add-declare.ftl b/len-activiti/src/main/resources/ftl/act/declare/add-declare.ftl
new file mode 100644
index 0000000..d51e133
--- /dev/null
+++ b/len-activiti/src/main/resources/ftl/act/declare/add-declare.ftl
@@ -0,0 +1,174 @@
+<#--Created by IntelliJ IDEA.
+User: zxm
+Date: 2017/12/20
+Time: 10:00
+To change this template use File | Settings | File Templates.-->
+
+
+
+
+
+
+ 新建业绩申报
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/len-activiti/src/main/resources/ftl/act/declare/declareList.ftl b/len-activiti/src/main/resources/ftl/act/declare/declareList.ftl
new file mode 100644
index 0000000..3bb8669
--- /dev/null
+++ b/len-activiti/src/main/resources/ftl/act/declare/declareList.ftl
@@ -0,0 +1,256 @@
+<#-- Created by IntelliJ IDEA.
+ User: zxm
+ Date: 2018/1/15
+ Time: 16:53
+ To change this template use File | Settings | File Templates.
+流程部署-->
+
+
+
+
+ 业绩申报流程
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <#--开始时间:-->
+ <#--
-->
+ <#-- -->
+ <#--
-->
+ <#--结束时间:-->
+ <#--
-->
+ <#-- -->
+ <#--
-->
+ <#--
-->
+ <#---->
+
+ ဂ
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/len-activiti/src/main/resources/ftl/act/declare/update-declare-readonly.ftl b/len-activiti/src/main/resources/ftl/act/declare/update-declare-readonly.ftl
new file mode 100644
index 0000000..594d80d
--- /dev/null
+++ b/len-activiti/src/main/resources/ftl/act/declare/update-declare-readonly.ftl
@@ -0,0 +1,165 @@
+<#--Created by IntelliJ IDEA.
+User: zxm
+Date: 2017/12/20
+Time: 10:00
+To change this template use File | Settings | File Templates.-->
+
+
+
+
+
+
+ 业绩申报查看
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/len-activiti/src/main/resources/ftl/act/declare/update-declare.ftl b/len-activiti/src/main/resources/ftl/act/declare/update-declare.ftl
new file mode 100644
index 0000000..f354f6d
--- /dev/null
+++ b/len-activiti/src/main/resources/ftl/act/declare/update-declare.ftl
@@ -0,0 +1,165 @@
+<#--Created by IntelliJ IDEA.
+User: zxm
+Date: 2017/12/20
+Time: 10:00
+To change this template use File | Settings | File Templates.-->
+
+
+
+
+
+
+ 业绩申报查看
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/len-activiti/src/main/resources/ftl/act/leave/leaveList.ftl b/len-activiti/src/main/resources/ftl/act/leave/leaveList.ftl
index 9d53488..8156ea9 100644
--- a/len-activiti/src/main/resources/ftl/act/leave/leaveList.ftl
+++ b/len-activiti/src/main/resources/ftl/act/leave/leaveList.ftl
@@ -174,17 +174,17 @@
if (obj.event === 'start') {
start(data.key);
}else if(obj.event === 'getProcImage'){
- var url='getProcImage?processInstanceId='+data.processInstanceId+'';
+// var url='getProcImage?processInstanceId='+data.processInstanceId+'';
layer.open({
id: 'leave-image',
- type: 1,
+ type: 2,
area: [ '880px', '400px'],
fix: false,
maxmin: true,
shadeClose: false,
shade: 0.4,
title: '流程图',
- content: " "
+ content: '${re.contextPath}/leave/shinePics/' + data.processInstanceId
});
}else if(obj.event==='leaveDetail'){
layer.open({
diff --git a/len-activiti/src/main/resources/ftl/act/leave/shinePics.ftl b/len-activiti/src/main/resources/ftl/act/leave/shinePics.ftl
new file mode 100644
index 0000000..89fef86
--- /dev/null
+++ b/len-activiti/src/main/resources/ftl/act/leave/shinePics.ftl
@@ -0,0 +1,71 @@
+<#--Created by IntelliJ IDEA.
+User: zxm
+Date: 2017/12/20
+Time: 10:00
+To change this template use File | Settings | File Templates.-->
+
+
+
+
+
+
+ 流程图
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/len-activiti/src/main/resources/static/editor-app/configuration/properties/assignment-popup-popup.html b/len-activiti/src/main/resources/static/editor-app/configuration/properties/assignment-popup-popup.html
new file mode 100644
index 0000000..f66c317
--- /dev/null
+++ b/len-activiti/src/main/resources/static/editor-app/configuration/properties/assignment-popup-popup.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{role.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/len-activiti/src/main/resources/static/editor-app/configuration/properties/assignment-popup.html b/len-activiti/src/main/resources/static/editor-app/configuration/properties/assignment-popup.html
index e4b42b3..5ea8ad4 100644
--- a/len-activiti/src/main/resources/static/editor-app/configuration/properties/assignment-popup.html
+++ b/len-activiti/src/main/resources/static/editor-app/configuration/properties/assignment-popup.html
@@ -10,7 +10,11 @@
@@ -18,7 +22,8 @@
{{'PROPERTY.ASSIGNMENT.CANDIDATE_USERS' | translate}}
-
+
+
@@ -27,7 +32,8 @@
{{'PROPERTY.ASSIGNMENT.CANDIDATE_GROUPS' | translate}}
-
+
+
diff --git a/len-activiti/src/main/resources/static/editor-app/libs/pagination/tm.pagination.js b/len-activiti/src/main/resources/static/editor-app/libs/pagination/tm.pagination.js
new file mode 100644
index 0000000..69fb0c3
--- /dev/null
+++ b/len-activiti/src/main/resources/static/editor-app/libs/pagination/tm.pagination.js
@@ -0,0 +1,203 @@
+/**
+ * name: tm.pagination
+ * Version: 0.0.2
+ */
+angular.module('tm.pagination', []).directive('tmPagination',[function(){
+ return {
+ restrict: 'EA',
+ template: '
' +
+ '' +
+ '
' +
+ '第 页 ' +
+ '每页 ' +
+ '/共{{ conf.totalItems }} 条' +
+ '
' +
+ '
暂无数据
' +
+ '
',
+ replace: true,
+ scope: {
+ conf: '='
+ },
+ link: function(scope, element, attrs){
+
+ // 变更当前页
+ scope.changeCurrentPage = function(item){
+ if(item == '...'){
+ return;
+ }else{
+ scope.conf.currentPage = item;
+ }
+ };
+
+ // 定义分页的长度必须为奇数 (default:9)
+ scope.conf.pagesLength = parseInt(scope.conf.pagesLength) ? parseInt(scope.conf.pagesLength) : 9 ;
+ if(scope.conf.pagesLength % 2 === 0){
+ // 如果不是奇数的时候处理一下
+ scope.conf.pagesLength = scope.conf.pagesLength -1;
+ }
+
+ // conf.erPageOptions
+ if(!scope.conf.perPageOptions){
+ scope.conf.perPageOptions = [10, 15, 20, 30, 50];
+ }
+
+ // pageList数组
+ function getPagination(){
+ // conf.currentPage
+ scope.conf.currentPage = parseInt(scope.conf.currentPage) ? parseInt(scope.conf.currentPage) : 1;
+ // conf.totalItems
+ scope.conf.totalItems = parseInt(scope.conf.totalItems);
+
+ // conf.itemsPerPage (default:15)
+ // 先判断一下本地存储中有没有这个值
+ if(scope.conf.rememberPerPage){
+ if(!parseInt(localStorage[scope.conf.rememberPerPage])){
+ localStorage[scope.conf.rememberPerPage] = parseInt(scope.conf.itemsPerPage) ? parseInt(scope.conf.itemsPerPage) : 15;
+ }
+
+ scope.conf.itemsPerPage = parseInt(localStorage[scope.conf.rememberPerPage]);
+
+
+ }else{
+ scope.conf.itemsPerPage = parseInt(scope.conf.itemsPerPage) ? parseInt(scope.conf.itemsPerPage) : 15;
+ }
+
+ // numberOfPages
+ scope.conf.numberOfPages = Math.ceil(scope.conf.totalItems/scope.conf.itemsPerPage);
+
+ // judge currentPage > scope.numberOfPages
+ if(scope.conf.currentPage < 1){
+ scope.conf.currentPage = 1;
+ }
+
+ if(scope.conf.currentPage > scope.conf.numberOfPages){
+ scope.conf.currentPage = scope.conf.numberOfPages;
+ }
+
+ // jumpPageNum
+ scope.jumpPageNum = scope.conf.currentPage;
+
+ // 如果itemsPerPage在不在perPageOptions数组中,就把itemsPerPage加入这个数组中
+ var perPageOptionsLength = scope.conf.perPageOptions.length;
+ // 定义状态
+ var perPageOptionsStatus;
+ for(var i = 0; i < perPageOptionsLength; i++){
+ if(scope.conf.perPageOptions[i] == scope.conf.itemsPerPage){
+ perPageOptionsStatus = true;
+ }
+ }
+ // 如果itemsPerPage在不在perPageOptions数组中,就把itemsPerPage加入这个数组中
+ if(!perPageOptionsStatus){
+ scope.conf.perPageOptions.push(scope.conf.itemsPerPage);
+ }
+
+ // 对选项进行sort
+ scope.conf.perPageOptions.sort(function(a, b){return a-b});
+
+ scope.pageList = [];
+ if(scope.conf.numberOfPages <= scope.conf.pagesLength){
+ // 判断总页数如果小于等于分页的长度,若小于则直接显示
+ for(i =1; i <= scope.conf.numberOfPages; i++){
+ scope.pageList.push(i);
+ }
+ }else{
+ // 总页数大于分页长度(此时分为三种情况:1.左边没有...2.右边没有...3.左右都有...)
+ // 计算中心偏移量
+ var offset = (scope.conf.pagesLength - 1)/2;
+ if(scope.conf.currentPage <= offset){
+ // 左边没有...
+ for(i =1; i <= offset +1; i++){
+ scope.pageList.push(i);
+ }
+ scope.pageList.push('...');
+ scope.pageList.push(scope.conf.numberOfPages);
+ }else if(scope.conf.currentPage > scope.conf.numberOfPages - offset){
+ scope.pageList.push(1);
+ scope.pageList.push('...');
+ for(i = offset + 1; i >= 1; i--){
+ scope.pageList.push(scope.conf.numberOfPages - i);
+ }
+ scope.pageList.push(scope.conf.numberOfPages);
+ }else{
+ // 最后一种情况,两边都有...
+ scope.pageList.push(1);
+ scope.pageList.push('...');
+
+ for(i = Math.ceil(offset/2) ; i >= 1; i--){
+ scope.pageList.push(scope.conf.currentPage - i);
+ }
+ scope.pageList.push(scope.conf.currentPage);
+ for(i = 1; i <= offset/2; i++){
+ scope.pageList.push(scope.conf.currentPage + i);
+ }
+
+ scope.pageList.push('...');
+ scope.pageList.push(scope.conf.numberOfPages);
+ }
+ }
+
+ if(scope.conf.onChange){
+ scope.conf.onChange();
+ }
+ scope.$parent.conf = scope.conf;
+ }
+
+ // prevPage
+ scope.prevPage = function(){
+ if(scope.conf.currentPage > 1){
+ scope.conf.currentPage -= 1;
+ }
+ };
+ // nextPage
+ scope.nextPage = function(){
+ if(scope.conf.currentPage < scope.conf.numberOfPages){
+ scope.conf.currentPage += 1;
+ }
+ };
+
+ // 跳转页
+ scope.jumpToPage = function(){
+ scope.jumpPageNum = scope.jumpPageNum.replace(/[^0-9]/g,'');
+ if(scope.jumpPageNum !== ''){
+ scope.conf.currentPage = scope.jumpPageNum;
+ }
+ };
+
+ // 修改每页显示的条数
+ scope.changeItemsPerPage = function(){
+ // 清除本地存储的值方便重新设置
+ if(scope.conf.rememberPerPage){
+ localStorage.removeItem(scope.conf.rememberPerPage);
+ }
+ };
+
+ scope.$watch(function(){
+ var newValue = scope.conf.currentPage + ' ' + scope.conf.totalItems + ' ';
+ // 如果直接watch perPage变化的时候,因为记住功能的原因,所以一开始可能调用两次。
+ //所以用了如下方式处理
+ if(scope.conf.rememberPerPage){
+ // 由于记住的时候需要特别处理一下,不然可能造成反复请求
+ // 之所以不监控localStorage[scope.conf.rememberPerPage]是因为在删除的时候会undefind
+ // 然后又一次请求
+ if(localStorage[scope.conf.rememberPerPage]){
+ newValue += localStorage[scope.conf.rememberPerPage];
+ }else{
+ newValue += scope.conf.itemsPerPage;
+ }
+ }else{
+ newValue += scope.conf.itemsPerPage;
+ }
+ return newValue;
+
+ }, getPagination);
+
+ }
+ };
+}]);
diff --git a/len-activiti/src/main/resources/static/modeler.html b/len-activiti/src/main/resources/static/modeler.html
index 0dde10d..9ae605c 100644
--- a/len-activiti/src/main/resources/static/modeler.html
+++ b/len-activiti/src/main/resources/static/modeler.html
@@ -25,6 +25,26 @@
+
+
@@ -90,7 +110,8 @@
-
+
+
@@ -112,7 +133,7 @@
-
+
diff --git a/len-activiti/src/main/resources/stencilset.json b/len-activiti/src/main/resources/stencilset.json
index 0389732..b49d4e0 100644
--- a/len-activiti/src/main/resources/stencilset.json
+++ b/len-activiti/src/main/resources/stencilset.json
@@ -718,7 +718,7 @@
"id" : "StartNoneEvent",
"title" : "事件",
"description" : "A start event without a specific trigger",
- "view" : "\n
\n \n \n \t \n \n \n \n\t \n \n ",
+ "view" : "\n
\n \n \n \t \n \n \n \n\t \n \n ",
"icon" : "startevent/none.png",
"groups" : [ "启动事件" ],
"propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage", "initiatorpackage", "formkeydefinitionpackage", "formpropertiespackage" ],
@@ -773,7 +773,7 @@
"id" : "UserTask",
"title" : "用户活动",
"description" : "分配给特定人的任务 ",
- "view" : "\n
\n \n \n \t \n \t \n \t \n \t\n \t \n \t \n \t \n \t\n \t \n \t \n \t \n \t\n \t \n \t \n \t \n \t\n \t \n \n \n\t \n\t \n\t\t\n\t\t \n\t\n\t\n\t\t \n\t\t\n\t \n \n\t\n\t\t \n\t \n\t\n\t\n\t\t \n\t \n\t\n\n\t\n\t\t \n\t \n \n ",
+ "view" : "\n
\n \n \n \t \n \t \n \t \n \t\n \t \n \t \n \t \n \t\n \t \n \t \n \t \n \t\n \t \n \t \n \t \n \t\n \t \n \n \n\t \n\t \n\t\t\n\t\t \n\t\n\t\n\t\t \n\t\t\n\t \n \n\t\n\t\t \n\t \n\t\n\t\n\t\t \n\t \n\t\n\n\t\n\t\t \n\t \n \n ",
"icon" : "activity/list/type.user.png",
"groups" : [ "活动列表" ],
"propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "asynchronousdefinitionpackage", "exclusivedefinitionpackage", "executionlistenerspackage", "multiinstance_typepackage", "multiinstance_cardinalitypackage", "multiinstance_collectionpackage", "multiinstance_variablepackage", "multiinstance_conditionpackage", "isforcompensationpackage", "usertaskassignmentpackage", "formkeydefinitionpackage", "duedatedefinitionpackage", "prioritydefinitionpackage", "formpropertiespackage", "tasklistenerspackage" ],
@@ -1081,7 +1081,7 @@
"id" : "EndNoneEvent",
"title" : "结束任务",
"description" : "一个无触发器的结束任务",
- "view" : "\n
\n \n \n \t \n \n \n \n\t \n \n ",
+ "view" : "\n
\n \n \n \t \n \n \n \n\t \n \n ",
"icon" : "endevent/none.png",
"groups" : [ "结束任务列表" ],
"propertyPackages" : [ "overrideidpackage", "namepackage", "documentationpackage", "executionlistenerspackage" ],
diff --git a/len-core/src/main/java/com/len/base/BaseService.java b/len-core/src/main/java/com/len/base/BaseService.java
index d8c1d1e..508e8a2 100644
--- a/len-core/src/main/java/com/len/base/BaseService.java
+++ b/len-core/src/main/java/com/len/base/BaseService.java
@@ -61,4 +61,5 @@ public interface BaseService
extends BaseMapper
public ReType show(T t, int page, int limit);
+ public String showAll(T t);
}
diff --git a/len-core/src/main/java/com/len/base/impl/BaseServiceImpl.java b/len-core/src/main/java/com/len/base/impl/BaseServiceImpl.java
index 237d65d..a4bc980 100644
--- a/len-core/src/main/java/com/len/base/impl/BaseServiceImpl.java
+++ b/len-core/src/main/java/com/len/base/impl/BaseServiceImpl.java
@@ -1,5 +1,6 @@
package com.len.base.impl;
+import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.len.base.BaseMapper;
@@ -144,46 +145,6 @@ public abstract class BaseServiceImpl implements Base
return getMappser().selectListByPage(record);
}
- @Override
- public int deleteByPrimaryKey(Object o) {
- return getMappser().deleteByPrimaryKey(o);
- }
-
- @Override
- public int delete(T t) {
- return getMappser().delete(t);
- }
-
- @Override
- public boolean existsWithPrimaryKey(Object o) {
- return getMappser().existsWithPrimaryKey(o);
- }
-
- @Override
- public T selectByPrimaryKey(Object o) {
- return getMappser().selectByPrimaryKey(o);
- }
-
- @Override
- public T selectOne(T t) {
- return getMappser().selectOne(t);
- }
-
- @Override
- public int deleteByIds(String s) {
- return getMappser().deleteByIds(s);
- }
-
- @Override
- public int insertList(List list) {
- return getMappser().insertList(list);
- }
-
- @Override
- public int insertUseGeneratedKeys(T t) {
- return getMappser().insertUseGeneratedKeys(t);
- }
-
/**
* 公共展示类
*
@@ -205,4 +166,19 @@ public abstract class BaseServiceImpl implements Base
return new ReType(tPage.getTotal(), tList);
}
+ @Override
+ public String showAll(T t)
+ {
+ List tList = null;
+ try {
+ tList = getMappser().selectListByPage(t);
+ } catch (MyException e) {
+// logger.error("class:BaseServiceImpl ->method:show->message:" + e.getMessage());
+ log.error("class:BaseServiceImpl ->method:show->message:" + e.getMessage());
+ e.printStackTrace();
+ }
+// ReType reType = new ReType( tList);
+ return JSON.toJSONString(tList);
+ }
+
}
diff --git a/len-sys/src/main/java/com/len/controller/RoleController.java b/len-sys/src/main/java/com/len/controller/RoleController.java
index 2788aca..80d16a7 100644
--- a/len-sys/src/main/java/com/len/controller/RoleController.java
+++ b/len-sys/src/main/java/com/len/controller/RoleController.java
@@ -65,6 +65,15 @@ public class RoleController extends BaseController {
return roleService.show(role, Integer.valueOf(page), Integer.valueOf(limit));
}
+ @ApiOperation(value = "/showaLLRoleList", httpMethod = "GET", notes = "展示角色")
+ @GetMapping(value = "showaLLRoleList")
+ @ResponseBody
+ @RequiresPermissions("role:show")
+ public String showRoleList(SysRole role, Model model) {
+ return roleService.showAll(role);
+ }
+
+
@GetMapping(value = "showAddRole")
public String goAddRole(Model model) {
JSONArray jsonArray = menuService.getTreeUtil(null);
diff --git a/len-sys/src/main/java/com/len/controller/UserController.java b/len-sys/src/main/java/com/len/controller/UserController.java
index 4a05914..14e07dc 100644
--- a/len-sys/src/main/java/com/len/controller/UserController.java
+++ b/len-sys/src/main/java/com/len/controller/UserController.java
@@ -1,5 +1,7 @@
package com.len.controller;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
import com.len.base.BaseController;
import com.len.core.annotation.Log;
import com.len.core.annotation.Log.LOG_TYPE;
@@ -73,6 +75,20 @@ public class UserController extends BaseController {
return userService.show(user, Integer.valueOf(page), Integer.valueOf(limit));
}
+ @ApiOperation(value = "/listByRoleId", httpMethod = "GET", notes = "展示角色")
+ @GetMapping(value = "listByRoleId")
+ @ResponseBody
+ @RequiresPermissions("user:show")
+ public String showUser(Model model, String roleId,int page, int limit) {
+ JSONObject returnValue = new JSONObject();
+ List users = userService.getUserByRoleId(roleId,page,limit);
+ int counts = userService.countUserByRoleId(roleId,page,limit);
+ returnValue.put("users",users);
+ returnValue.put("totals",counts);
+ return JSON.toJSONString(returnValue);
+ }
+
+
@GetMapping(value = "showAddUser")
public String goAddUser(Model model) {
List checkboxList = userService.getUserRoleByJson(null);
diff --git a/len-sys/src/main/java/com/len/core/quartz/MySchedulerListener.java b/len-sys/src/main/java/com/len/core/quartz/MySchedulerListener.java
index 34ea3e1..d2dac25 100644
--- a/len-sys/src/main/java/com/len/core/quartz/MySchedulerListener.java
+++ b/len-sys/src/main/java/com/len/core/quartz/MySchedulerListener.java
@@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
@@ -16,7 +17,8 @@ public class MySchedulerListener {
@Bean(name ="schedulerFactoryBean")
public SchedulerFactoryBean schedulerFactory() {
SchedulerFactoryBean bean = new SchedulerFactoryBean();
- bean.setJobFactory(myJobFactory);
+ bean.setJobFactory(myJobFactory);
+ bean.setConfigLocation(new ClassPathResource("quartz.properties"));
return bean;
}
diff --git a/len-sys/src/main/java/com/len/mapper/SysUserMapper.java b/len-sys/src/main/java/com/len/mapper/SysUserMapper.java
index 11a3c6b..2365167 100644
--- a/len-sys/src/main/java/com/len/mapper/SysUserMapper.java
+++ b/len-sys/src/main/java/com/len/mapper/SysUserMapper.java
@@ -5,6 +5,9 @@ import org.apache.ibatis.annotations.Param;
import tk.mybatis.mapper.common.BaseMapper;
import tk.mybatis.mapper.common.Mapper;
+import java.util.List;
+import java.util.Map;
+
public interface SysUserMapper extends com.len.base.BaseMapper {
SysUser login(@Param("username") String username);
@@ -23,4 +26,7 @@ public interface SysUserMapper extends com.len.base.BaseMapper {
* @return
*/
int rePass(SysUser user);
+
+ List getUserByRoleId(Map map);
+ int countUserByRoleId(Map map);
}
\ No newline at end of file
diff --git a/len-sys/src/main/java/com/len/service/SysUserService.java b/len-sys/src/main/java/com/len/service/SysUserService.java
index 7d90d4f..1cf3b85 100644
--- a/len-sys/src/main/java/com/len/service/SysUserService.java
+++ b/len-sys/src/main/java/com/len/service/SysUserService.java
@@ -61,4 +61,8 @@ public interface SysUserService extends BaseService {
*/
int rePass(SysUser user);
+
+ List getUserByRoleId(String roleId,int page,int limit);
+
+ int countUserByRoleId(String roleId,int page,int limit);
}
diff --git a/len-sys/src/main/java/com/len/service/impl/SysUserServiceImpl.java b/len-sys/src/main/java/com/len/service/impl/SysUserServiceImpl.java
index 0dd9eec..3389c32 100644
--- a/len-sys/src/main/java/com/len/service/impl/SysUserServiceImpl.java
+++ b/len-sys/src/main/java/com/len/service/impl/SysUserServiceImpl.java
@@ -15,7 +15,10 @@ import com.len.util.Checkbox;
import com.len.util.JsonUtil;
import com.len.util.Md5Util;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -180,4 +183,24 @@ public class SysUserServiceImpl extends BaseServiceImpl implemen
public int rePass(SysUser user) {
return sysUserMapper.rePass(user);
}
+
+ @Override
+ public List getUserByRoleId(String roleId,int page,int limit)
+ {
+ Map map = new HashMap<>();
+ map.put("roleId",roleId);
+ map.put("page",(page-1)*limit );
+ map.put("limit",limit);
+ return sysUserMapper.getUserByRoleId(map);
+ }
+
+ @Override
+ public int countUserByRoleId(String roleId,int page,int limit)
+ {
+ Map map = new HashMap<>();
+ map.put("roleId",roleId);
+ map.put("page",(page-1)*limit );
+ map.put("limit",limit);
+ return sysUserMapper.countUserByRoleId(map);
+ }
}
diff --git a/len-sys/src/main/resources/ftl/login.ftl b/len-sys/src/main/resources/ftl/login.ftl
index bd44232..2e01ea8 100644
--- a/len-sys/src/main/resources/ftl/login.ftl
+++ b/len-sys/src/main/resources/ftl/login.ftl
@@ -23,13 +23,13 @@
len-脚手架
-
+
-
验证码:
+
验证码
@@ -79,7 +79,7 @@
layer.msg(msg, {icon: 5,anim:6,offset: 't'});
}
$("#code").click(function(){
- var url = "/getCode?"+new Date().getTime();
+ var url = "${re.contextPath}/getCode?"+new Date().getTime();
this.src = url;
}).click().show();
$('#code').on('mouseover',function(){
diff --git a/len-sys/src/main/resources/ftl/main/main.ftl b/len-sys/src/main/resources/ftl/main/main.ftl
index 1be2fcf..b336515 100644
--- a/len-sys/src/main/resources/ftl/main/main.ftl
+++ b/len-sys/src/main/resources/ftl/main/main.ftl
@@ -53,14 +53,14 @@
<#assign currentUser = Session["curentUser"]>
- ${currentUser.username}
+ ${currentUser.username}
基本资料
安全设置
-
注销
+
注销
diff --git a/len-sys/src/main/resources/ftl/system/menu/add-menu.ftl b/len-sys/src/main/resources/ftl/system/menu/add-menu.ftl
index b01ec6d..8570bd4 100644
--- a/len-sys/src/main/resources/ftl/system/menu/add-menu.ftl
+++ b/len-sys/src/main/resources/ftl/system/menu/add-menu.ftl
@@ -147,7 +147,7 @@ To change this template use File | Settings | File Templates.-->
shade: 0.4,
zIndex: layer.zIndex,
title: '图标',
- content: '/plugin/html/icon.html'
+ content: '../plugin/html/icon.html'
});
});
//自定义验证规则
diff --git a/len-sys/src/main/resources/ftl/system/menu/update-menu.ftl b/len-sys/src/main/resources/ftl/system/menu/update-menu.ftl
index 0db4289..19cdbc7 100644
--- a/len-sys/src/main/resources/ftl/system/menu/update-menu.ftl
+++ b/len-sys/src/main/resources/ftl/system/menu/update-menu.ftl
@@ -148,7 +148,7 @@ To change this template use File | Settings | File Templates.-->
shade: 0.4,
zIndex: layer.zIndex,
title: '图标',
- content: '/plugin/html/icon.html'
+ content: '../plugin/html/icon.html'
});
});
diff --git a/len-sys/src/main/resources/mapper/SysUserMapper.xml b/len-sys/src/main/resources/mapper/SysUserMapper.xml
index 617687f..7b7f830 100644
--- a/len-sys/src/main/resources/mapper/SysUserMapper.xml
+++ b/len-sys/src/main/resources/mapper/SysUserMapper.xml
@@ -139,4 +139,15 @@
update sys_user set password=#{password,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}
+
+
+
+ select sysuser.id,sysuser.real_name,sysuser.username from sys_user sysuser inner join sys_role_user roleuser on sysuser.id = roleuser.user_id and roleuser.role_id = #{roleId,jdbcType=VARCHAR}
+ limit #{page},#{limit}
+
+
+
+ select count(1) from sys_user sysuser inner join sys_role_user roleuser on sysuser.id = roleuser.user_id and roleuser.role_id = #{roleId,jdbcType=VARCHAR}
+
+
\ No newline at end of file
diff --git a/len-sys/src/main/resources/plugin/html/icon.html b/len-sys/src/main/resources/plugin/html/icon.html
index 02b6e18..7d9d3e8 100644
--- a/len-sys/src/main/resources/plugin/html/icon.html
+++ b/len-sys/src/main/resources/plugin/html/icon.html
@@ -3,10 +3,10 @@
图标
-
-
-
-
+
+
+
+