package com.mossle.bpm.calendar;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.joda.time.DateTime;
/*
org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior.java 在处理task的duedate时使用了DueDateBusinessCalendar
org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl.java 把BusinessCalendar的实现注册到BusinesssCalendarManager中
org.activiti.engine.impl.jobexecutor.TimerDeclarationImpl.java 是从BusinessCalendarManager里根据type获取的
org.activiti.engine.impl.jobexecutor.TimerDeclarationType.java 是对BusinessCalendar的类型进行的注册
org.activiti.engine.impl.persistence.entity.TimerEntity.java 是从BusinessCalendarManager里获取的CycleBusinessCalendar
R3/2011/2012 以R开头就是重复多次,R后面是重复次数,如果没有次数,默认无限循环
2011/2012 从2011开始,到2012截止
2011/P1D 从2011开始,一天后截止
P1D/2012 从一天后开始,2012截止
P1D 从一天后开始,无截止时间
UserTaskActivityBehavior的场景,如果P1D,就是持续1天,如果2012,就是持续到2012
TimerDeclarationImpl的场景,可能是duration也可能是dueDate,也可能是cycle,如果是cycle,保存时会把当前时间作为开始时间
TimerEntity的场景,只有为cycle时,才重新进行计算下一次的触发时间
*/
public class DurationUtil {
Date start;
Date end;
Duration period;
boolean isRepeat;
int times;
DatatypeFactory datatypeFactory;
private boolean useBusinessTime;
private AdvancedBusinessCalendar businessCalendar;
public DurationUtil(String text, AdvancedBusinessCalendar businessCalendar)
throws Exception {
this.businessCalendar = businessCalendar;
this.useBusinessTime = text.indexOf("business") != -1;
if (useBusinessTime) {
text = text.substring("business".length()).trim();
}
List<String> expressions = Arrays.asList(text.split("/"));
this.datatypeFactory = DatatypeFactory.newInstance();
if ((expressions.size() > 3) || expressions.isEmpty()) {
throw new ActivitiIllegalArgumentException("Cannot parse duration");
}
// 获得重复次数
if (expressions.get(0).startsWith("R")) {
this.isRepeat = true;
this.times = (expressions.get(0).length() == 1) ? Integer.MAX_VALUE
: Integer.parseInt(expressions.get(0).substring(1));
expressions = expressions.subList(1, expressions.size());
}
// 如果是P开头的,说明是时间段
if (this.isDuration(expressions.get(0))) {
// 先计算时间段
this.period = this.parsePeriod(expressions.get(0));
// 如果有后半部分,就是结束时间
// 如果没有,就可能是无限循环
this.end = (expressions.size() == 1) ? null : this
.parseDate(expressions.get(1));
} else {
// 如果不是P开头,就是开始时间
this.start = this.parseDate(expressions.get(0));
if (this.isDuration(expressions.get(1))) {
// 如果后半段是P开头的时间段,就解析时间段
this.period = this.parsePeriod(expressions.get(1));
} else {
// 如果后半段是结束时间,时间段就是end-start
this.end = this.parseDate(expressions.get(1));
this.period = this.datatypeFactory.newDuration(this.end
.getTime() - this.start.getTime());
}
}
// 如果只设置了一个时间段,既没有开始也没有结束时间
if ((this.start == null) && (this.end == null)) {
// 就把当前时间设置为开始时间
this.start = new Date();
}
}
public Date getDateAfter() {
if (this.isRepeat) {
return this.getDateAfterRepeat(new Date());
}
// TODO: is this correct?
if (this.end != null) {
return this.end;
}
return this.add(this.start, this.period);
}
public int getTimes() {
return this.times;
}
private Date getDateAfterRepeat(Date date) {
if (this.start != null) {
Date cur = this.start;
for (int i = 0; (i < this.times) && !cur.after(date); i++) {
cur = add(cur, this.period);
}
return cur.before(date) ? null : cur;
}
Date cur = this.add(this.end, this.period.negate());
Date next = this.end;
for (int i = 0; (i < this.times) && cur.after(date); i++) {
next = cur;
cur = this.add(cur, this.period.negate());
}
return next.before(date) ? null : next;
}
private Date add(Date date, Duration duration) {
if (!useBusinessTime) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
duration.addTo(calendar);
return calendar.getTime();
}
return businessCalendar.add(date, duration, useBusinessTime);
}
private Date parseDate(String text) throws Exception {
Date date = DateTime.parse(text).toDate();
if (!this.useBusinessTime) {
return date;
}
return businessCalendar.processDate(date, useBusinessTime);
}
private Duration parsePeriod(String period) throws Exception {
return datatypeFactory.newDuration(period);
}
private boolean isDuration(String time) {
return time.startsWith("P");
}
}