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.--> + + + + + + + 流程图 + + + + + + + + + + + +
+
+
+
+ + +
+ + +
+ + + + diff --git a/len-activiti/src/main/resources/ftl/act/leave/update-leave-readonly.ftl b/len-activiti/src/main/resources/ftl/act/leave/update-leave-readonly.ftl new file mode 100644 index 0000000..a544e77 --- /dev/null +++ b/len-activiti/src/main/resources/ftl/act/leave/update-leave-readonly.ftl @@ -0,0 +1,143 @@ +<#--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/task/task-agent-iframe.ftl b/len-activiti/src/main/resources/ftl/act/task/task-agent-iframe.ftl new file mode 100644 index 0000000..fbcc287 --- /dev/null +++ b/len-activiti/src/main/resources/ftl/act/task/task-agent-iframe.ftl @@ -0,0 +1,95 @@ +<#--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/task/taskList.ftl b/len-activiti/src/main/resources/ftl/act/task/taskList.ftl index e7096bb..f61d61f 100644 --- a/len-activiti/src/main/resources/ftl/act/task/taskList.ftl +++ b/len-activiti/src/main/resources/ftl/act/task/taskList.ftl @@ -156,7 +156,7 @@ if (obj.event === 'handle') { popup('办理','agent/'+data.id,700,500,'task-agent'); }else if(obj.event === 'update'){ - popup('编辑','updateLeave/'+data.id,700,500,'task-update'); + popup('编辑','${re.contextPath}/leave/updateLeave/'+data.id,700,500,'task-update'); }else if(obj.event==='leaveDetail'){ layer.open({ id: 'leave-detail', @@ -167,7 +167,7 @@ shadeClose: false, shade: 0.4, title: '审核详情', - content: "/leave/leaveDetail?processId="+data.processInstanceId + content: "leaveDetail?processId="+data.processInstanceId }); } }); diff --git a/len-activiti/src/main/resources/mapper/ActAssigneeMapper.xml b/len-activiti/src/main/resources/mapper/ActAssigneeMapper.xml index 3005353..50a3ce1 100644 --- a/len-activiti/src/main/resources/mapper/ActAssigneeMapper.xml +++ b/len-activiti/src/main/resources/mapper/ActAssigneeMapper.xml @@ -12,23 +12,20 @@ + + id, sid, assignee, role_id, assignee_type, activti_name + + delete from act_assignee where sid = #{sid,jdbcType=VARCHAR} - - \ No newline at end of file diff --git a/len-activiti/src/main/resources/static/editor-app/app-cfg.js b/len-activiti/src/main/resources/static/editor-app/app-cfg.js index fef58bb..676ca12 100644 --- a/len-activiti/src/main/resources/static/editor-app/app-cfg.js +++ b/len-activiti/src/main/resources/static/editor-app/app-cfg.js @@ -21,5 +21,5 @@ var ACTIVITI = ACTIVITI || {}; ACTIVITI.CONFIG = { - 'contextRoot' : '/service', + 'contextRoot' : '../service', }; diff --git a/len-activiti/src/main/resources/static/editor-app/app.js b/len-activiti/src/main/resources/static/editor-app/app.js index f20bc54..b56cec2 100644 --- a/len-activiti/src/main/resources/static/editor-app/app.js +++ b/len-activiti/src/main/resources/static/editor-app/app.js @@ -28,7 +28,8 @@ var activitiModeler = angular.module('activitiModeler', [ 'ngGrid', 'ngAnimate', 'pascalprecht.translate', - 'duScroll' + 'duScroll', + 'tm.pagination' ]); var activitiModule = activitiModeler; diff --git a/len-activiti/src/main/resources/static/editor-app/configuration/properties-assignment-controller.js b/len-activiti/src/main/resources/static/editor-app/configuration/properties-assignment-controller.js index 6fbfc20..0ed69ae 100644 --- a/len-activiti/src/main/resources/static/editor-app/configuration/properties-assignment-controller.js +++ b/len-activiti/src/main/resources/static/editor-app/configuration/properties-assignment-controller.js @@ -20,7 +20,7 @@ /* * Assignment */ -var KisBpmAssignmentCtrl = [ '$scope', '$modal', function($scope, $modal) { +var KisBpmAssignmentCtrl = [ '$scope', '$modal','$http', function($scope, $modal,$http) { // Config for the modal window var opts = { @@ -32,7 +32,7 @@ var KisBpmAssignmentCtrl = [ '$scope', '$modal', function($scope, $modal) { $modal(opts); }]; -var KisBpmAssignmentPopupCtrl = [ '$scope', function($scope) { +var KisBpmAssignmentPopupCtrl = [ '$scope','$modal', function($scope, $modal) { // Put json representing assignment on scope if ($scope.property.value !== undefined && $scope.property.value !== null @@ -48,6 +48,12 @@ var KisBpmAssignmentPopupCtrl = [ '$scope', function($scope) { { $scope.assignment.candidateUsers = [{value: ''}]; } + + + if ($scope.assignment.assignee == undefined || $scope.assignment.assignee == '') + { + $scope.assignment.assignee = ''; + } // Click handler for + button after enum value var userValueIndex = 1; @@ -75,6 +81,28 @@ var KisBpmAssignmentPopupCtrl = [ '$scope', function($scope) { $scope.assignment.candidateGroups.splice(index, 1); }; + //Open the dialog to select users + $scope.choseAssignment = function(flag) { + + var opts = { + template: 'editor-app/configuration/properties/assignment-popup-popup.html?version=' + Date.now(), + scope: $scope + }; + $scope.choseAssignmentFlag = flag; + // Open the dialog + $modal(opts); + } + + //Open the dialog to select candidateGroups + $scope.choseCandidateGroups = function(){ + var opts = { + template: 'editor-app/configuration/properties/assignment-candidateGroup.html?version=' + Date.now(), + scope: $scope + }; + // Open the dialog + $modal(opts); + } + $scope.save = function() { $scope.property.value = {}; @@ -147,4 +175,242 @@ var KisBpmAssignmentPopupCtrl = [ '$scope', function($scope) { } } }; -}]; \ No newline at end of file + + //因新打开的界面上选定的数据要传输到当前modal中,所以使用此方式,这是angular.js中不同控制器之间传输数据的方式 + $scope.$on('choseAssigneesStr', function(event,data,nameData){ + var infos = data.split(","); + var nameInfos =nameData.split(","); + // $scope.assignment.candidateUsers= []; + for(var i=0;i + +
\ 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 @@ + \ 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 @@
- + +
@@ -27,7 +32,8 @@
- + +
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: '
' + + '
    ' + + '
  • «
  • ' + + '
  • ' + + '{{ item }}' + + '
  • ' + + '
  • »
  • ' + + '
' + + '
' + + '第页 ' + + '每页' + + '/共{{ 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} + + + + + + \ 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 @@ 图标 - - - - + + + +