Java tutorial
/** * Genji Scrum Tool and Issue Tracker * Copyright (C) 2015 Steinbeis GmbH & Co. KG Task Management Solutions * <a href="http://www.trackplus.com">Genji Scrum Tool</a> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* $Id:$ */ package com.aurel.track.linkType; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.joda.time.DateTime; import org.joda.time.Days; import com.aurel.track.admin.customize.category.filter.execute.loadItems.LoadItemIDListItems; import com.aurel.track.beans.TWorkItemBean; import com.aurel.track.beans.TWorkItemLinkBean; import com.aurel.track.calendar.WorkDaysConfig; import com.aurel.track.calendar.WorkDaysConfigImplementation; import com.aurel.track.calendar.WorkItemWithNewCascadedDates; import com.aurel.track.dao.DAOFactory; import com.aurel.track.dao.WorkItemLinkDAO; import com.aurel.track.errors.ErrorData; import com.aurel.track.exchange.msProject.exchange.MsProjectExchangeDataStoreBean; import com.aurel.track.exchange.msProject.exchange.MsProjectExchangeDataStoreBean.PREDECESSOR_ELEMENT_TYPE; import com.aurel.track.exchange.msProject.importer.LinkLagBL; import com.aurel.track.fieldType.constants.SystemFields; import com.aurel.track.fieldType.runtime.base.FieldsManagerRT; import com.aurel.track.fieldType.runtime.base.WorkItemContext; import com.aurel.track.item.ItemBL; import com.aurel.track.item.ItemLoaderException; import com.aurel.track.item.link.ItemLinkBL; import com.aurel.track.prop.ApplicationBean; public class MsProjectLinkTypeBL { public static Logger LOGGER = LogManager.getLogger(MsProjectLinkTypeBL.class); private static WorkItemLinkDAO workItemLinkDao = DAOFactory.getFactory().getWorkItemLinkDAO(); /** * In case of a changed work item violates one or more pred. dependency: this method returns a map containing violated dependencies id. * (This method check if actual (changed) work item new start/end date is valid when actual work item is represented as a succ.) * @param startDate * @param endDate * @param workItemParam * @return * @throws ItemLoaderException */ public static Set<Integer> checkSuccMoveValidity(Date startDate, Date endDate, Integer workItemID, Integer personID, TWorkItemLinkBean actualCreatedLink) { Map<Integer, Integer> msProjectIdMap = new HashMap<Integer, Integer>(); MsProjectLinkType msProjectLinkType = MsProjectLinkType.getInstance(); List<Integer> msProjectLink = LinkTypeBL.getLinkTypesByPluginClass(msProjectLinkType); for (int i = 0; i < msProjectLink.size(); i++) { int type = (Integer) msProjectLink.get(i); msProjectIdMap.put(type, type); } Set<Integer> problems = new HashSet<Integer>(); List<TWorkItemLinkBean> links = workItemLinkDao.loadByWorkItemSucc(workItemID); if (actualCreatedLink != null) { links.add(actualCreatedLink); } int[] workItemIds; ArrayList<Integer> workItemIdsList = new ArrayList<Integer>(); for (TWorkItemLinkBean actualLinkBean : links) { if (msProjectIdMap.containsKey(actualLinkBean.getLinkType())) { workItemIdsList.add(actualLinkBean.getLinkPred()); } } workItemIds = new int[workItemIdsList.size()]; for (int i = 0; i < workItemIdsList.size(); i++) { workItemIds[i] = workItemIdsList.get(i); } List<TWorkItemBean> predList = new ArrayList<TWorkItemBean>(); predList = LoadItemIDListItems.getWorkItemBeansByWorkItemIDs(workItemIds, personID, false, false, false); Map<Integer, TWorkItemBean> predMap = new HashMap<Integer, TWorkItemBean>(); for (int i = 0; i < predList.size(); i++) { predMap.put(predList.get(i).getObjectID(), predList.get(i)); } for (TWorkItemLinkBean actualLinkBean : links) { if (msProjectIdMap.containsKey(actualLinkBean.getLinkType())) { Integer dependencyType = actualLinkBean.getIntegerValue1(); int predWorkItemId = actualLinkBean.getLinkPred(); TWorkItemBean predWorkItemBean = predMap.get(predWorkItemId); Date predWorkItemStartDate = getStartDate(predWorkItemBean); //in case of milestone work item end date is missing Date predWorkItemEndDate = getEndDate(predWorkItemBean); if (predWorkItemEndDate != null && predWorkItemStartDate != null) { if (predWorkItemBean.isMilestone()) { predWorkItemEndDate = predWorkItemStartDate; } if (actualLinkBean.getLinkLag() != 0 && checkLinkLagAvaibilityForCascade(actualLinkBean.getLinkLagFormat())) { int linkLagInDays = getLinkLagInDays(actualLinkBean, workItemID); if (linkLagInDays > 0) { predWorkItemEndDate = stepForward(predWorkItemEndDate, linkLagInDays, true); predWorkItemStartDate = stepBack(predWorkItemStartDate, linkLagInDays, true); } else if (linkLagInDays < 0) { predWorkItemEndDate = stepBack(predWorkItemEndDate, Math.abs(linkLagInDays), true); predWorkItemStartDate = stepForward(predWorkItemStartDate, Math.abs(linkLagInDays), true); } } if (dependencyType != null) { switch (dependencyType) { case PREDECESSOR_ELEMENT_TYPE.FS: if (predWorkItemEndDate.compareTo(startDate) >= 0) { if (!problems.contains(actualLinkBean.getObjectID())) { problems.add(actualLinkBean.getObjectID()); } } break; case PREDECESSOR_ELEMENT_TYPE.FF: if (predWorkItemEndDate.compareTo(endDate) >= 0) { if (!problems.contains(actualLinkBean.getObjectID())) { problems.add(actualLinkBean.getObjectID()); } } break; case PREDECESSOR_ELEMENT_TYPE.SF: if (predWorkItemStartDate.compareTo(endDate) > 0) { if (!problems.contains(actualLinkBean.getObjectID())) { problems.add(actualLinkBean.getObjectID()); } } break; case PREDECESSOR_ELEMENT_TYPE.SS: if (predWorkItemStartDate.compareTo(startDate) > 0) { if (!problems.contains(actualLinkBean.getObjectID())) { problems.add(actualLinkBean.getObjectID()); } } break; } } } } } return problems; } /** * This method checks if the parameters work item's new start/end date dependencies are valid with his preds. * @param originalMovedWorkItemBean * @param workItemParam * @param startdateParam * @param endDateParam * @param workItemLinkDao * @return * @throws ItemLoaderException */ public static boolean checkWorkItemAndPredsDependencyViolation(TWorkItemBean originalMovedWorkItemBean, TWorkItemBean workItemParam, Date startdateParam, Date endDateParam, Integer personID, Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap, List<Integer> msProjectLinkTypeIDs, Date startDate, Date endDate) throws ItemLoaderException { boolean violates = false; List<TWorkItemLinkBean> links = workItemLinkDao.loadByWorkItemSucc(workItemParam.getObjectID()); List<Integer> workItemIDsList = new ArrayList<Integer>(); for (TWorkItemLinkBean workItemLinkBean : links) { if (msProjectLinkTypeIDs.contains(workItemLinkBean.getLinkType())) { workItemIDsList.add(workItemLinkBean.getLinkPred()); } } int[] workItemIDs = org.apache.commons.lang.ArrayUtils .toPrimitive(workItemIDsList.toArray(new Integer[workItemIDsList.size()])); List<TWorkItemBean> predList = LoadItemIDListItems.getWorkItemBeansByWorkItemIDs(workItemIDs, personID, false, false, false); Map<Integer, TWorkItemBean> predMap = new HashMap<Integer, TWorkItemBean>(); for (int i = 0; i < predList.size(); i++) { predMap.put(predList.get(i).getObjectID(), predList.get(i)); } for (int i = 0; i < links.size(); i++) { TWorkItemLinkBean actualLinkBean = links.get(i); if (msProjectLinkTypeIDs.contains(actualLinkBean.getLinkType())) { int dependencyType = actualLinkBean.getIntegerValue1(); int predWorkItemId = actualLinkBean.getLinkPred(); TWorkItemBean predWorkItemBean = predMap.get(predWorkItemId); Date predWorkItemStartDate = getStartDate(predWorkItemBean); Date predWorkItemEndDate = getEndDate(predWorkItemBean); if (originalMovedWorkItemBean.getObjectID().equals(predWorkItemBean.getObjectID())) { predWorkItemStartDate = startDate; predWorkItemEndDate = endDate; } if (itemCascadeMap.get(predWorkItemId) == null && predWorkItemId != originalMovedWorkItemBean.getObjectID()) { switch (dependencyType) { case PREDECESSOR_ELEMENT_TYPE.FS: if (predWorkItemEndDate != null && predWorkItemEndDate.compareTo(startdateParam) >= 0) { violates = true; } break; case PREDECESSOR_ELEMENT_TYPE.FF: if (predWorkItemEndDate != null && predWorkItemEndDate.compareTo(endDateParam) >= 0) { violates = true; } break; case PREDECESSOR_ELEMENT_TYPE.SF: if (predWorkItemStartDate != null && predWorkItemStartDate.compareTo(endDateParam) > 0) { violates = true; } break; case PREDECESSOR_ELEMENT_TYPE.SS: if (predWorkItemStartDate != null && predWorkItemStartDate.compareTo(startdateParam) > 0) { violates = true; } break; } } } } return violates; } /** * This method returns true if the link lag unit is: d(day), mo(month), w(week) * @param linkLagType * @return */ public static boolean checkLinkLagAvaibilityForCascade(int linkLagType) { if (MsProjectExchangeDataStoreBean.LAG_FORMAT.d == linkLagType || MsProjectExchangeDataStoreBean.LAG_FORMAT.mo == linkLagType || MsProjectExchangeDataStoreBean.LAG_FORMAT.w == linkLagType) { return true; } return false; } /** * This method returns true if actualWorkItem new start/end dates require cascading childWorkItem. * This method returns false if actualWorkItem new start/end dates not violating dependency with childWorkItem => childWorkItem doesn't needs cascading. * @param actualWorkItem * @param childWorkItem * @param link * @return */ public static boolean checkWhichChildWorkItemNeedCascadingRight(TWorkItemBean actualWorkItem, TWorkItemBean childWorkItem, TWorkItemLinkBean link, Date startDate, Date endDate) { boolean cascadeNeed = false; if (getStartDate(childWorkItem) == null && getEndDate(childWorkItem) == null) { return cascadeNeed; } int dependencyType = link.getIntegerValue1(); Date newStartDate = startDate; Date newEndDate = endDate; if (actualWorkItem.isMilestone()) { newEndDate = newStartDate; } if (link.getLinkLag() != 0 && checkLinkLagAvaibilityForCascade(link.getLinkLagFormat())) { int linkLagInDays = getLinkLagInDays(link, actualWorkItem.getObjectID()); if (linkLagInDays > 0) { newEndDate = stepForward(newEndDate, linkLagInDays, true); newStartDate = stepBack(newStartDate, linkLagInDays, true); } } switch (dependencyType) { case PREDECESSOR_ELEMENT_TYPE.FS: if (newEndDate.compareTo(getStartDate(childWorkItem)) >= 0) { cascadeNeed = true; } break; case PREDECESSOR_ELEMENT_TYPE.FF: if (newEndDate.compareTo(getEndDate(childWorkItem)) >= 0) { cascadeNeed = true; } break; case PREDECESSOR_ELEMENT_TYPE.SF: if (newStartDate.compareTo(getEndDate(childWorkItem)) > 0) { cascadeNeed = true; } break; case PREDECESSOR_ELEMENT_TYPE.SS: if (newStartDate.compareTo(getStartDate(childWorkItem)) > 0) { cascadeNeed = true; } break; } return cascadeNeed; } /** * This method subtracts steps from dateParam and returns. If withCheckingFreeDays == true * then weekends, and free days doesn't treats as work day. * @param dateParam * @param steps * @param withCheckingFreeDays * @return */ public static Date stepBack(Date dateParam, int steps, boolean withCheckingFreeDays) { if (dateParam == null) { return null; } int actualSteps = 0; Calendar cal = Calendar.getInstance(); cal.setTime(dateParam); Date newDate = new Date(); while (actualSteps < steps) { cal.add(Calendar.DAY_OF_YEAR, -1); newDate = cal.getTime(); if (withCheckingFreeDays) { if (!WorkDaysConfigImplementation.isSaturday(newDate) && !WorkDaysConfigImplementation.isSunday(newDate) && !WorkDaysConfigImplementation.isFreeDay(newDate)) { actualSteps++; } } else { actualSteps++; } } return newDate; } /** * This method adds steps to dateParam and returns. If withCheckingFreeDays == true * then weekends, and free days doesn't treats as work day. * @param dateParam * @param steps * @param withCheckingFreeDays * @return */ public static Date stepForward(Date dateParam, int steps, boolean withCheckingFreeDays) { if (dateParam == null) { return null; } int actualSteps = 0; Calendar cal = Calendar.getInstance(); cal.setTime(dateParam); Date newDate = new Date(); while (actualSteps < steps) { cal.add(Calendar.DAY_OF_YEAR, 1); newDate = cal.getTime(); if (withCheckingFreeDays) { if (!WorkDaysConfigImplementation.isSaturday(newDate) && !WorkDaysConfigImplementation.isSunday(newDate) && !WorkDaysConfigImplementation.isFreeDay(newDate)) { actualSteps++; } } else { actualSteps++; } } return newDate; } /** * This method returns true if workItemBean has parent(s). * @param workItemBean * @return */ public static boolean hasParent(List<Integer> msProjectLinkTypeIDs, Integer workItemID) { List<TWorkItemLinkBean> workItemLinkBeans = workItemLinkDao.loadByWorkItemSucc(workItemID); if (workItemLinkBeans != null && !workItemLinkBeans.isEmpty()) { for (TWorkItemLinkBean workItemLinkBean : workItemLinkBeans) { if (msProjectLinkTypeIDs.contains(workItemLinkBean.getLinkType())) { return true; } } } return false; } /** * This method returns true if actualWorkItem is a leaf. * @param actualWorkItem * @return */ public static boolean isLeaf(TWorkItemBean actualWorkItem, List<Integer> msProjectLinkTypeIDs) { List<TWorkItemLinkBean> workItemLinkBeans = workItemLinkDao .loadByWorkItemPred(actualWorkItem.getObjectID()); if (workItemLinkBeans != null && workItemLinkBeans.size() != 0) { for (TWorkItemLinkBean workItemLinkBean : workItemLinkBeans) { if (msProjectLinkTypeIDs.contains(workItemLinkBean.getLinkType())) { return false; } } } return true; } /** * Returns number of free days from given interval, start date included always, endDate only if includeLastDay == true! * @param startDateParam * @param endDateParam * @param includeLastDay * @return */ public static Integer getNumberOfDaysBetweenDates(Date startDateParam, Date endDateParam, boolean includeLastDay) { DateTime dateTime1 = new DateTime(startDateParam); DateTime dateTime2 = new DateTime(endDateParam); if (includeLastDay) { dateTime2 = dateTime2.plusDays(1); } int numberOfDays = Days.daysBetween(dateTime1, dateTime2).getDays(); return numberOfDays; } /** * Returns number of workdays from interval(startDateParam -> endDateParam). * @param startDateParam * @param endDateParam * @return */ public static int getNumberOfWorkDaysFromMovedIntervall(Date startDateParam, Date endDateParam) { int numberOfDays = getNumberOfDaysBetweenDates(startDateParam, endDateParam, false); if (!WorkDaysConfig.SATURDAY_WORK_DAY) { numberOfDays = numberOfDays - getNumberOfSaturdaysFromInterval(startDateParam, endDateParam, false); } if (!WorkDaysConfig.SUNDAY_WORK_DAY) { numberOfDays = numberOfDays - getNumberOfSundaysFromInterval(startDateParam, endDateParam, false); } numberOfDays = numberOfDays - getNumberOfFreeDaysFromIntervall(startDateParam, endDateParam); return numberOfDays; } /** * Returns number of saturdays from interval. Start date included always, endDate only if includeLastDay == true! * @param startDateParam * @param endDateParam * @param includeLastDay * @return */ public static Integer getNumberOfSaturdaysFromInterval(Date startDateParam, Date endDateParam, boolean includeLastDay) { Date realEndDate = endDateParam; if (includeLastDay) { Calendar cal = Calendar.getInstance(); cal.setTime(realEndDate); cal.add(Calendar.DAY_OF_YEAR, 1); realEndDate = cal.getTime(); } Calendar calendar = Calendar.getInstance(); calendar.setTime(startDateParam); int numOfSaturdays = 0; while (!calendar.getTime().after(realEndDate)) { if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) { numOfSaturdays++; } calendar.add(Calendar.DAY_OF_MONTH, 1); } return numOfSaturdays; } /** * Returns number of sundays from interval. Start date included always, endDate only if includeLastDay == true! * @param startDateParam * @param endDateParam * @param includeLastDay * @return */ public static Integer getNumberOfSundaysFromInterval(Date startDateParam, Date endDateParam, boolean includeLastDay) { Date realEndDate = endDateParam; if (includeLastDay) { Calendar cal = Calendar.getInstance(); cal.setTime(realEndDate); cal.add(Calendar.DAY_OF_YEAR, 1); realEndDate = cal.getTime(); } Calendar calendar = Calendar.getInstance(); calendar.setTime(startDateParam); int numOfSunday = 0; while (!calendar.getTime().after(realEndDate)) { if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) { numOfSunday++; } calendar.add(Calendar.DAY_OF_MONTH, 1); } return numOfSunday; } /** * Returns number of days from interval. start date included always, endDate only if includeLastDay == true! * @param startDateParam * @param endDateParam * @return */ public static Integer getNumberOfFreeDaysFromIntervall(Date startDateParam, Date endDateParam) { int numberOfFreeDays = 0; for (Map.Entry<Date, String> entry : WorkDaysConfig.exceptionFromWorkDays.entrySet()) { if (entry.getKey().getTime() <= endDateParam.getTime() && entry.getKey().getTime() >= startDateParam.getTime()) { numberOfFreeDays++; } } return numberOfFreeDays; } /** * Returns number of workdays from given interval, including start and end date * @param startDateParam * @param endDateParam * @return */ public static int getNumberOfWorkDaysBetweenDates(Date startDateParam, Date endDateParam) { int numberOfDays = getNumberOfDaysBetweenDates(startDateParam, endDateParam, true); if (!WorkDaysConfig.SATURDAY_WORK_DAY) { numberOfDays = numberOfDays - getNumberOfSaturdaysFromInterval(startDateParam, endDateParam, false); } if (!WorkDaysConfig.SUNDAY_WORK_DAY) { numberOfDays = numberOfDays - getNumberOfSundaysFromInterval(startDateParam, endDateParam, false); } numberOfDays = numberOfDays - getNumberOfFreeDaysFromIntervall(startDateParam, endDateParam); return numberOfDays; } /** * This method checks if the new start/end dates is valid. (not violating some preds. dependency) * If new state is not valid then workItemBean and workItemBean's all succss will not be saved.s * @param workItemBean */ //TODO Remove db. access from cycle public static void leftMovePredDependencyViolation(TWorkItemBean workItemBean, Integer personID, Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap, List<Integer> msProjectLinkTypeIDs, Date startDate, Date endDate) { for (Map.Entry<Integer, WorkItemWithNewCascadedDates> entry : itemCascadeMap.entrySet()) { if (entry.getValue().isSave()) { try { if (checkWorkItemAndPredsDependencyViolation(workItemBean, entry.getValue().getWorkItemBean(), entry.getValue().getStarDate(), entry.getValue().getEndDate(), personID, itemCascadeMap, msProjectLinkTypeIDs, startDate, endDate)) { entry.getValue().setSave(false); int actualWorkItemID = entry.getValue().getWorkItemBean().getObjectID(); List<TWorkItemLinkBean> links = workItemLinkDao.loadByWorkItemPred(actualWorkItemID); if (links.size() > 0) { for (int i = 0; i < links.size(); i++) { itemCascadeMap.get(links.get(i).getLinkSucc()).setSave(false); } } } } catch (ItemLoaderException e) { LOGGER.error("Cascading error: " + e.getMessage()); LOGGER.debug(ExceptionUtils.getStackTrace(e)); } } } } /** * This method saves into database all cascaded work items, where save flag is true. */ public static void storeAllChangedBean(Locale locale, Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap, Integer personID, List<Integer> msProjectLinkTypeIDs) { for (Map.Entry<Integer, WorkItemWithNewCascadedDates> entry : itemCascadeMap.entrySet()) { if (entry.getValue().isSave()) { Set<Integer> presentField = new HashSet<Integer>(); setPresentedFields(presentField); WorkItemContext workItemContext = FieldsManagerRT.getExchangeContextForExisting( entry.getValue().getWorkItemBean(), personID, locale, presentField); workItemContext.setExchangeImport(false); setStartDate(workItemContext.getWorkItemBean(), entry.getValue().getStarDate()); setEndDate(workItemContext.getWorkItemBean(), entry.getValue().getEndDate()); if (entry.getValue().getWorkItemBean().isMilestone()) { setEndDate(workItemContext.getWorkItemBean(), null); } List<ErrorData> errorsList = new LinkedList<ErrorData>(); boolean result = FieldsManagerRT.performSave(workItemContext, errorsList, false, false/*, false*/); LOGGER.debug("Work Item: " + entry.getKey() + " drag drop/resize saved: Errors: " + errorsList + " Result: " + result); } } } /** * This method stores work items from touchedWorkItem list, in case of this work item not exists or isSave flag is false from itemCascadeMap. * @param touchedWorkItem * @param personID * @param locale * @param msProjectLinkTypeIDs * @param itemCascadeMap */ private static void storeAllTouchedDescendantWorkItem(List<WorkItemWithNewCascadedDates> touchedWorkItem, Integer personID, Locale locale, List<Integer> msProjectLinkTypeIDs, Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap) { for (WorkItemWithNewCascadedDates entry : touchedWorkItem) { planItem(entry.getWorkItemBean(), getStartDate(entry.getWorkItemBean()), getEndDate(entry.getWorkItemBean()), entry.getStarDate(), entry.getEndDate(), locale, msProjectLinkTypeIDs, personID, false); boolean isSave = true; if (itemCascadeMap != null) { if (itemCascadeMap.get(entry.getWorkItemBean().getObjectID()) != null) { if (!itemCascadeMap.get(entry.getWorkItemBean().getObjectID()).isSave()) { isSave = false; } } } if (isSave) { Set<Integer> presentField = new HashSet<Integer>(); presentField.add(SystemFields.INTEGER_STARTDATE); presentField.add(SystemFields.INTEGER_ENDDATE); WorkItemContext workItemContext = FieldsManagerRT .getExchangeContextForExisting(entry.getWorkItemBean(), personID, locale, presentField); workItemContext.setExchangeImport(false); setStartDate(workItemContext.getWorkItemBean(), entry.getStarDate()); setEndDate(workItemContext.getWorkItemBean(), entry.getEndDate()); if (entry.getWorkItemBean().isMilestone()) { setEndDate(workItemContext.getWorkItemBean(), null); } List<ErrorData> errorsList = new LinkedList<ErrorData>(); boolean result = FieldsManagerRT.performSave(workItemContext, errorsList, false, false/*, false*/); LOGGER.debug("Work Item: " + entry.getWorkItemBean().getObjectID() + " drag drop/resize saved: Errors: " + errorsList + " Result: " + result); } } } /** * This method parses workItemBeanParam all successors recursively, cascades work items if needed. * @param workItemBeanParam * @param numberOfWorkedDaysFromMovedDays * @param numberOfMovedDays * @throws ItemLoaderException */ private static void parseLinks(TWorkItemBean workItemBeanParam, int numberOfWorkedDaysFromMovedDays, int numberOfMovedDays, Integer personID, Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap, List<Integer> msProjectLinkTypeIDs, List<TWorkItemBean> linkedDescendantChildren) throws ItemLoaderException { List<TWorkItemLinkBean> linksList = workItemLinkDao.loadByWorkItemPred(workItemBeanParam.getObjectID()); List<Integer> workItemsIDSList = new ArrayList<Integer>(); for (TWorkItemLinkBean workItemLinkBean : linksList) { if (msProjectLinkTypeIDs.contains(workItemLinkBean.getLinkType())) { workItemsIDSList.add(workItemLinkBean.getLinkSucc()); } } int[] workItemIDs = org.apache.commons.lang.ArrayUtils .toPrimitive(workItemsIDSList.toArray(new Integer[workItemsIDSList.size()])); List<TWorkItemBean> succList = LoadItemIDListItems.getWorkItemBeansByWorkItemIDs(workItemIDs, personID, false, false, false); for (int i = 0; i < succList.size(); i++) { TWorkItemBean newWorkItemBean = succList.get(i); if (getStartDate(newWorkItemBean) != null && getEndDate(newWorkItemBean) != null) { if (newWorkItemBean.isMilestone()) { setEndDate(newWorkItemBean, getStartDate(newWorkItemBean)); } linkedDescendantChildren.add(newWorkItemBean); if (itemCascadeMap.get(newWorkItemBean.getObjectID()) != null) { if (itemCascadeMap.get(newWorkItemBean.getObjectID()).isNeedsCascade()) { int newWorkItemWorkDaysNumber = getNumberOfWorkDaysBetweenDates( getStartDate(newWorkItemBean), getEndDate(newWorkItemBean)); WorkItemWithNewCascadedDates tmp = new WorkItemWithNewCascadedDates(); if (numberOfMovedDays < 0) { tmp.setStarDate(stepBack(getStartDate(newWorkItemBean), Math.abs(numberOfWorkedDaysFromMovedDays), true)); if (Math.abs(newWorkItemWorkDaysNumber) == 1) { tmp.setEndDate(tmp.getStarDate()); } else { tmp.setEndDate(stepBack(getEndDate(newWorkItemBean), Math.abs(numberOfWorkedDaysFromMovedDays), true)); } tmp.setWorkItemBean(newWorkItemBean); tmp.setNeedsCascade(false); tmp.setSave(true); itemCascadeMap.put(newWorkItemBean.getObjectID(), tmp); } else { tmp.setStarDate(stepForward(getStartDate(newWorkItemBean), Math.abs(numberOfWorkedDaysFromMovedDays), true)); if (newWorkItemWorkDaysNumber == 1) { tmp.setEndDate(tmp.getStarDate()); } else { tmp.setEndDate(stepForward(tmp.getStarDate(), newWorkItemWorkDaysNumber - 1, true)); } tmp.setWorkItemBean(newWorkItemBean); tmp.setNeedsCascade(false); tmp.setSave(true); itemCascadeMap.put(newWorkItemBean.getObjectID(), tmp); } parseLinks(newWorkItemBean, numberOfWorkedDaysFromMovedDays, numberOfMovedDays, personID, itemCascadeMap, msProjectLinkTypeIDs, linkedDescendantChildren); } } else { int newWorkItemWorkDaysNumber = getNumberOfWorkDaysBetweenDates(getStartDate(newWorkItemBean), getEndDate(newWorkItemBean)); WorkItemWithNewCascadedDates tmp = new WorkItemWithNewCascadedDates(); if (numberOfMovedDays < 0) { tmp.setStarDate(stepBack(getStartDate(newWorkItemBean), Math.abs(numberOfWorkedDaysFromMovedDays), true)); if (newWorkItemWorkDaysNumber == 1) { tmp.setEndDate(tmp.getStarDate()); } else { tmp.setEndDate(stepForward(tmp.getStarDate(), newWorkItemWorkDaysNumber - 1, true)); } tmp.setWorkItemBean(newWorkItemBean); tmp.setNeedsCascade(false); tmp.setSave(true); itemCascadeMap.put(newWorkItemBean.getObjectID(), tmp); } else { tmp.setStarDate(stepForward(getStartDate(newWorkItemBean), Math.abs(numberOfWorkedDaysFromMovedDays), true)); if (newWorkItemWorkDaysNumber == 1) { tmp.setEndDate(tmp.getStarDate()); } else { tmp.setEndDate(stepForward(tmp.getStarDate(), newWorkItemWorkDaysNumber - 1, true)); } tmp.setWorkItemBean(newWorkItemBean); tmp.setNeedsCascade(false); tmp.setSave(true); itemCascadeMap.put(newWorkItemBean.getObjectID(), tmp); } parseLinks(newWorkItemBean, numberOfWorkedDaysFromMovedDays, numberOfMovedDays, personID, itemCascadeMap, msProjectLinkTypeIDs, linkedDescendantChildren); } } } } /** * This method check moved/resied work item direction (left or right), and based on direction * calls helper methods for cascading the successor work items. * @param workItemBean * @param numberOfWorkedDaysFromMovedDays * @param numberOfMovedDays * @return * @throws ItemLoaderException */ private static Map<String, Date> cascadeWorkItemChanges(TWorkItemBean workItemBean, int numberOfWorkedDaysFromMovedDays, int numberOfMovedDays, Integer personID, Locale locale, Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap, List<Integer> msProjectLinkTypeIDs, Integer workItemID, Date startDate, Date endDate, boolean storeCurrentWorkitem) throws ItemLoaderException { ArrayList<TWorkItemBean> linkedDescendantChildren = new ArrayList<TWorkItemBean>(); List<TWorkItemLinkBean> links = workItemLinkDao.loadByWorkItemPred(workItemID); int[] workItemIDs; List<Integer> workItemIDsList = new ArrayList<Integer>(); for (TWorkItemLinkBean workItemLinkBean : links) { if (msProjectLinkTypeIDs.contains(workItemLinkBean.getLinkType())) { workItemIDsList.add(workItemLinkBean.getLinkSucc()); } } workItemIDs = org.apache.commons.lang.ArrayUtils .toPrimitive(workItemIDsList.toArray(new Integer[workItemIDsList.size()])); List<TWorkItemBean> succList = LoadItemIDListItems.getWorkItemBeansByWorkItemIDs(workItemIDs, personID, false, false, false); if (numberOfMovedDays < 0) { for (int i = 0; i < succList.size(); i++) { TWorkItemBean childWorkItem = succList.get(i); WorkItemWithNewCascadedDates tmp = new WorkItemWithNewCascadedDates(); tmp.setWorkItemBean(childWorkItem); tmp.setEndDate(getEndDate(childWorkItem)); tmp.setStarDate(getStartDate(childWorkItem)); tmp.setNeedsCascade(true); itemCascadeMap.put(childWorkItem.getObjectID(), tmp); } parseLinks(workItemBean, numberOfWorkedDaysFromMovedDays, numberOfMovedDays, personID, itemCascadeMap, msProjectLinkTypeIDs, linkedDescendantChildren); leftMovePredDependencyViolation(workItemBean, personID, itemCascadeMap, msProjectLinkTypeIDs, startDate, endDate); } else { if (succList.size() > 0) { for (int i = 0; i < succList.size(); i++) { TWorkItemBean childWorkItem = succList.get(i); if (checkWhichChildWorkItemNeedCascadingRight(workItemBean, childWorkItem, links.get(i), startDate, endDate)) { WorkItemWithNewCascadedDates tmp = new WorkItemWithNewCascadedDates(); tmp.setWorkItemBean(childWorkItem); tmp.setEndDate(getEndDate(childWorkItem)); tmp.setStarDate(getStartDate(childWorkItem)); tmp.setNeedsCascade(true); itemCascadeMap.put(childWorkItem.getObjectID(), tmp); } else { WorkItemWithNewCascadedDates tmp = new WorkItemWithNewCascadedDates(); tmp.setWorkItemBean(childWorkItem); tmp.setEndDate(getEndDate(childWorkItem)); tmp.setStarDate(getStartDate(childWorkItem)); tmp.setNeedsCascade(false); itemCascadeMap.put(childWorkItem.getObjectID(), tmp); } } } parseLinks(workItemBean, numberOfWorkedDaysFromMovedDays, numberOfMovedDays, personID, itemCascadeMap, msProjectLinkTypeIDs, linkedDescendantChildren); } for (TWorkItemBean bean : linkedDescendantChildren) { WorkItemWithNewCascadedDates tmp = itemCascadeMap.get(bean.getObjectID()); if (tmp.isSave()) { checkChildrens(bean.getObjectID(), tmp.getWorkItemBean(), personID, getStartDate(tmp.getWorkItemBean()), getEndDate(tmp.getWorkItemBean()), tmp.getStarDate(), tmp.getEndDate(), locale, msProjectLinkTypeIDs, itemCascadeMap); } } storeAllChangedBean(locale, itemCascadeMap, personID, msProjectLinkTypeIDs); return null; } /** * This method remove links. THe link id's are in Set. * @param conflictingLinkIDs */ public static void removeViolatedParentDependencies(Set<Integer> conflictingLinkIDs) { if (conflictingLinkIDs != null) { for (Integer conflictingLinkID : conflictingLinkIDs) { ItemLinkBL.deleteLink(conflictingLinkID); } } } /** * This method returns all superior work items for the given parameter. * @param parentID * @param workItemBean * @return */ public static Set<TWorkItemBean> checkParent(Integer parentID, TWorkItemBean workItemBean) { Set<TWorkItemBean> parents = new HashSet<TWorkItemBean>(); while (parentID != null) { TWorkItemBean parentWorkItem = null; try { parentWorkItem = ItemBL.loadWorkItem(parentID); if (parentWorkItem != null) { parents.add(parentWorkItem); } } catch (ItemLoaderException e) { LOGGER.error("Error: " + e.getMessage()); } parentID = null; parentID = parentWorkItem.getSuperiorworkitem(); } return parents; } /** * This method returns a work item all descendants. * @param childrens * @param allDescendant * @param workItemID */ public static void getAllChildrens(List<TWorkItemBean> childrens, List<TWorkItemBean> allDescendant, Integer workItemID) { for (TWorkItemBean bean : childrens) { allDescendant.add(bean); List<TWorkItemBean> newChildrens = ItemBL.getChildren(bean.getObjectID()); if (newChildrens.size() > 0) { getAllChildrens(newChildrens, allDescendant, workItemID); } } } /** * This method creates a data set with work items and descendant's * WorkItemID=>WorkItemBean * =>Set of children work items * @param movedWorkItemAllDescendants * @param changedWorkItem * @return */ private static Map<Integer, Map<TWorkItemBean, Set<TWorkItemBean>>> createWorkItemIDToWorkItemToChildIDMap( List<TWorkItemBean> movedWorkItemAllDescendants, TWorkItemBean changedWorkItem) { Map<Integer, Map<TWorkItemBean, Set<TWorkItemBean>>> workItemIDToWorkItemToChildIDMap = new LinkedHashMap<Integer, Map<TWorkItemBean, Set<TWorkItemBean>>>(); Map<TWorkItemBean, Set<TWorkItemBean>> changedWorkItemToChildIDMap = new LinkedHashMap<TWorkItemBean, Set<TWorkItemBean>>(); Set<TWorkItemBean> changedWorkItemChildsBeanSet = new HashSet<TWorkItemBean>(); changedWorkItemToChildIDMap.put(changedWorkItem, changedWorkItemChildsBeanSet); workItemIDToWorkItemToChildIDMap.put(changedWorkItem.getObjectID(), changedWorkItemToChildIDMap); for (TWorkItemBean bean : movedWorkItemAllDescendants) { if (workItemIDToWorkItemToChildIDMap.get(bean.getSuperiorworkitem()) != null) { TWorkItemBean key = workItemIDToWorkItemToChildIDMap.get(bean.getSuperiorworkitem()).keySet() .iterator().next(); workItemIDToWorkItemToChildIDMap.get(bean.getSuperiorworkitem()).get(key).add(bean); } else { Map<TWorkItemBean, Set<TWorkItemBean>> newParentToBeanMap = new LinkedHashMap<TWorkItemBean, Set<TWorkItemBean>>(); Set<TWorkItemBean> newParentWorkItemChildsBeanSet = new HashSet<TWorkItemBean>(); newParentWorkItemChildsBeanSet.add(bean); TWorkItemBean parent = null; for (TWorkItemBean parentWorkItem : movedWorkItemAllDescendants) { if (parentWorkItem.getObjectID().intValue() == bean.getSuperiorworkitem().intValue()) { parent = parentWorkItem; break; } } newParentToBeanMap.put(parent, newParentWorkItemChildsBeanSet); workItemIDToWorkItemToChildIDMap.put(parent.getObjectID(), newParentToBeanMap); } } return workItemIDToWorkItemToChildIDMap; } /** * This method checks if moving a parent work item and descendant's violates MS. Proj. link dependency. * In case of violation returns a Set with violated Dependencies, otherwise saves moved work items and * cascades all descendants. In case of one descendant work item is linked the successor work item * will be cascaded. * * @param workItemID * @param movedWorkItem * @param personID * @param oldStartDate * @param oldEndDate * @param startDate * @param endDate * @param locale * @param msProjectLinkTypeIDs * @param itemCascadeMap * @return */ private static Set<Integer> checkChildrens(Integer workItemID, TWorkItemBean movedWorkItem, Integer personID, Date oldStartDate, Date oldEndDate, Date startDate, Date endDate, Locale locale, List<Integer> msProjectLinkTypeIDs, Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap) { Set<Integer> violatedDependencies = new HashSet<Integer>(); List<TWorkItemBean> childrens = ItemBL.getChildren(workItemID); if (childrens.isEmpty()) { return violatedDependencies; } int numberOfMovedDays = 0; int numberOfWorkedDaysFromMovedDays = 0; if (oldStartDate.compareTo(startDate) != 0 && oldEndDate.compareTo(endDate) != 0) { numberOfMovedDays = getNumberOfDaysBetweenDates(oldStartDate, startDate, false); if (numberOfMovedDays < 0) { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(endDate, oldEndDate); } else { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(oldStartDate, startDate); } } List<WorkItemWithNewCascadedDates> touchedDescendantWorkItems = new ArrayList<WorkItemWithNewCascadedDates>(); List<TWorkItemBean> allDescendant = new ArrayList<TWorkItemBean>(); getAllChildrens(childrens, allDescendant, workItemID); Map<Integer, Map<TWorkItemBean, Set<TWorkItemBean>>> workItemIDToWorkItemToChildIDMap = createWorkItemIDToWorkItemToChildIDMap( allDescendant, movedWorkItem); for (Map.Entry<Integer, Map<TWorkItemBean, Set<TWorkItemBean>>> entry : workItemIDToWorkItemToChildIDMap .entrySet()) { Map<TWorkItemBean, Set<TWorkItemBean>> parentToChild = entry.getValue(); TWorkItemBean parent = parentToChild.keySet().iterator().next(); Set<TWorkItemBean> childs = parentToChild.get(parent); for (TWorkItemBean childWorkItem : childs) { Set<Integer> violatedPredDependencies = checkSuccMoveValidity(getStartDate(childWorkItem), getEndDate(childWorkItem), childWorkItem.getObjectID(), personID, null); if (violatedPredDependencies.isEmpty()) { if (getStartDate(childWorkItem) != null && getEndDate(childWorkItem) != null) { Date[] newStartEndDate = cascadeWorkItemBySuperior(numberOfMovedDays, numberOfWorkedDaysFromMovedDays, childWorkItem); Set<Integer> problems = checkSuccMoveValidity(newStartEndDate[0], newStartEndDate[1], childWorkItem.getObjectID(), personID, null); if (problems.size() > 0) { for (Integer id : problems) { violatedDependencies.add(id); } } WorkItemWithNewCascadedDates tmp = new WorkItemWithNewCascadedDates(); tmp.setWorkItemBean(childWorkItem); tmp.setStarDate(newStartEndDate[0]); tmp.setEndDate(newStartEndDate[1]); touchedDescendantWorkItems.add(tmp); } } } } if (violatedDependencies.isEmpty()) { storeAllTouchedDescendantWorkItem(touchedDescendantWorkItems, personID, locale, msProjectLinkTypeIDs, null); return violatedDependencies; } else { return violatedDependencies; } } /** * This method check if a child work item is moved and the actualized parent violates MS. Proj. * dependency. * @param workItemBean * @param startDate * @param endDate * @param personID * @return */ public static Set<Integer> validateAncestorBottomUpLinks(TWorkItemBean workItemBean, Date startDate, Date endDate, Integer personID) { Integer parentID = workItemBean.getSuperiorworkitem(); Set<Integer> problems = new HashSet<Integer>(); while (parentID != null) { try { TWorkItemBean parentWorkItem = ItemBL.loadWorkItem(parentID); checkSuccMoveValidity(startDate, endDate, workItemBean.getObjectID(), personID, null); Set<Integer> tmp = checkSuccMoveValidity(startDate, endDate, parentWorkItem.getObjectID(), personID, null); problems.addAll(tmp); parentID = parentWorkItem.getSuperiorworkitem(); } catch (ItemLoaderException e) { } } return problems; } /** * This method cascades child work items. * @param numberOfMovedDays * @param numberOfWorkedDaysFromMovedDays * @param child * @return */ public static Date[] cascadeWorkItemBySuperior(int numberOfMovedDays, int numberOfWorkedDaysFromMovedDays, TWorkItemBean child) { int newWorkItemWorkDaysNumber = getNumberOfWorkDaysBetweenDates(getStartDate(child), getEndDate(child)); Date[] startEndDate = new Date[2]; if (numberOfMovedDays < 0) { startEndDate[0] = stepBack(getStartDate(child), Math.abs(numberOfWorkedDaysFromMovedDays), true); if (Math.abs(newWorkItemWorkDaysNumber) == 1) { startEndDate[1] = startEndDate[0]; } else { startEndDate[1] = stepForward(startEndDate[0], newWorkItemWorkDaysNumber - 1, true); } } else { startEndDate[0] = stepForward(getStartDate(child), Math.abs(numberOfWorkedDaysFromMovedDays), true); if (newWorkItemWorkDaysNumber == 1) { startEndDate[1] = startEndDate[0]; } else { startEndDate[1] = stepForward(startEndDate[0], newWorkItemWorkDaysNumber - 1, true); } } return startEndDate; } /** * This method saves work items new start/end date in case of drag and drop, resizing and cascades dependent work items in case. * New start and end date is in startDate and endDate attribute. * Bryntum increase end date with one day: for ex. work item start date: 2013-11-25 and the duration is 3 days => the end date is: 2013-11-29 * Because Bryntum increase end date with one day when saving new start/end date it must be reduced with one day. * (In proper method when returning the JSON for bryntum the endDate must be increased with one day!) * The bean contains reduced interval, the parameters (startDate, endDate) contains increased, Bryntum compatible interval. * This method checks moved work item and his preds violation, in case returns proper error message. * @param workItemBean * @param startDateParam * @param endDateParam * @param sessionParam * @param localeParam * @param removeViolatedLinksAndCascade * @param wrongDependencies * @param msProjectIdMapParam * @param personBeanParam * @param servletResponse * @return */ public static void planItem(TWorkItemBean workItemBean, Date oldStartDate, Date oldEndDate, Date startDate, Date endDate, Locale locale, List<Integer> msProjectLinkTypeIDs, Integer personID, boolean storeCurrentWorkitem) { Integer workItemID = workItemBean.getObjectID(); Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap = new HashMap<Integer, WorkItemWithNewCascadedDates>(); int numberOfMovedDays = 0; int numberOfWorkedDaysFromMovedDays = 0; if (workItemBean.isMilestone()) { setEndDate(workItemBean, getStartDate(workItemBean)); endDate = startDate; } boolean onlyStartDateChanged = false; try { if (startDate != null && endDate != null && oldStartDate != null && oldEndDate != null) { if (oldStartDate.compareTo(startDate) != 0 && oldEndDate.compareTo(endDate) != 0) { numberOfMovedDays = getNumberOfDaysBetweenDates(oldStartDate, startDate, false); if (numberOfMovedDays < 0) { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(endDate, oldEndDate); } else { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(oldStartDate, startDate); } } else { if (oldStartDate.compareTo(startDate) != 0) { numberOfMovedDays = getNumberOfDaysBetweenDates(oldStartDate, startDate, false); onlyStartDateChanged = true; if (numberOfMovedDays < 0) { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(startDate, oldStartDate); } else { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(oldStartDate, startDate); } } if (oldEndDate.compareTo(endDate) != 0) { numberOfMovedDays = getNumberOfDaysBetweenDates(oldEndDate, endDate, false); if (numberOfMovedDays < 0) { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(endDate, oldEndDate); } else { numberOfWorkedDaysFromMovedDays = getNumberOfWorkDaysFromMovedIntervall(oldEndDate, endDate); } } } if (numberOfWorkedDaysFromMovedDays < 0) { numberOfWorkedDaysFromMovedDays = Math.abs(numberOfWorkedDaysFromMovedDays); } if (!onlyStartDateChanged) { cascadeWorkItemChanges(workItemBean, numberOfWorkedDaysFromMovedDays, numberOfMovedDays, personID, locale, itemCascadeMap, msProjectLinkTypeIDs, workItemID, startDate, endDate, storeCurrentWorkitem); } } } catch (Exception ex) { LOGGER.error("Error: " + ex.getMessage()); if (LOGGER.isDebugEnabled()) { LOGGER.error(ExceptionUtils.getStackTrace(ex)); } } } /** * In Gantt we are handling link lags: day, week, month, * This method returns link lag in days. * Foe ex: -1) 1 mo link lag is 20 work day. * -2) 1 w link lag is 5 work days * @param actualLinkBean * @param workItemID * @return */ public static int getLinkLagInDays(TWorkItemLinkBean actualLinkBean, Integer workItemID) { Double hoursPerWorkday = MsProjectLinkType.getHoursPerWorkingDayForWorkItem(workItemID); Double convertedLinkLag = LinkLagBL.getUILinkLagFromMinutes(actualLinkBean.getLinkLag(), actualLinkBean.getLinkLagFormat(), hoursPerWorkday); int linkLagInDays = 0; Integer actualLinkLagFormat = actualLinkBean.getLinkLagFormat(); if (actualLinkLagFormat == null) { actualLinkLagFormat = MsProjectExchangeDataStoreBean.LAG_FORMAT.d; } switch (actualLinkLagFormat) { case MsProjectExchangeDataStoreBean.LAG_FORMAT.d: case MsProjectExchangeDataStoreBean.LAG_FORMAT.ed: linkLagInDays = convertedLinkLag.intValue(); break; case MsProjectExchangeDataStoreBean.LAG_FORMAT.mo: case MsProjectExchangeDataStoreBean.LAG_FORMAT.emo: linkLagInDays = convertedLinkLag.intValue() * 20; break; case MsProjectExchangeDataStoreBean.LAG_FORMAT.w: case MsProjectExchangeDataStoreBean.LAG_FORMAT.ew: linkLagInDays = convertedLinkLag.intValue() * 5; break; } return linkLagInDays; } /** * This method saves actual link bean after drag and drop/resize. * This method calls perform save which will call cascade mechanism. * @param workItemBean * @param personID * @param locale * @param startDate * @param endDate */ private static void saveActualBean(TWorkItemBean workItemBean, Integer personID, Locale locale, Date startDate, Date endDate) { if (startDate != null || endDate != null) { Set<Integer> presentField = new HashSet<Integer>(); setPresentedFields(presentField); WorkItemContext workItemContext = FieldsManagerRT.getExchangeContextForExisting(workItemBean, personID, locale, presentField); workItemContext.setExchangeImport(false); setStartDate(workItemContext.getWorkItemBean(), startDate); if (workItemContext.getWorkItemBean().isMilestone()) { setEndDate(workItemContext.getWorkItemBean(), null); } else { setEndDate(workItemContext.getWorkItemBean(), endDate); } List<ErrorData> errorsList = new LinkedList<ErrorData>(); boolean result = FieldsManagerRT.performSave(workItemContext, errorsList, false, false/*, true*/); LOGGER.debug("Work Item: " + workItemBean.getObjectID() + " drag drop/resize saved: Errors: " + errorsList + " Result: " + result); } } /** * Based on link format: FF, FS, SF, SS this method check number of work days between two linked tasks. * For ex.: Task1 successor is Task2. Task1 end date is 2014-01-20 and Task2 start date is 2014-01-23. * Task1 and Task2 is linked with FS link type. In this case this method returns 2 because between this two tasks * is 2 work days. It is important when setting link lag to 1 days, in this case do not cascade Task2. * @param link * @param pred * @param succ * @return */ private static Integer getNumberOfDaysBetweenTaskBasedOnLinkType(TWorkItemLinkBean link, TWorkItemBean pred, TWorkItemBean succ) { int dependencyType = link.getIntegerValue1(); int numberOfWorkDays = 0; Calendar cal; Date predDate; Date succDate; //the start dates are pushed back one day switch (dependencyType) { case PREDECESSOR_ELEMENT_TYPE.FS: succDate = getStartDate(succ); if (succDate == null) { return null; } cal = Calendar.getInstance(); cal.setTime(succDate); cal.add(Calendar.DAY_OF_YEAR, -1); succDate = cal.getTime(); predDate = getEndDate(pred); if (predDate == null) { return null; } numberOfWorkDays = getNumberOfWorkDaysBetweenDates(predDate, succDate); break; case PREDECESSOR_ELEMENT_TYPE.FF: predDate = getEndDate(pred); if (predDate == null) { return null; } succDate = getEndDate(succ); if (succDate == null) { return null; } numberOfWorkDays = getNumberOfWorkDaysBetweenDates(predDate, succDate); break; case PREDECESSOR_ELEMENT_TYPE.SF: predDate = getStartDate(pred); if (predDate == null) { return null; } cal = Calendar.getInstance(); cal.setTime(predDate); cal.add(Calendar.DAY_OF_YEAR, -1); predDate = cal.getTime(); succDate = getEndDate(succ); if (succDate == null) { return null; } numberOfWorkDays = getNumberOfWorkDaysBetweenDates(predDate, succDate); break; case PREDECESSOR_ELEMENT_TYPE.SS: predDate = getStartDate(pred); if (predDate == null) { return null; } cal = Calendar.getInstance(); cal.setTime(predDate); cal.add(Calendar.DAY_OF_YEAR, -1); predDate = cal.getTime(); succDate = getStartDate(succ); if (succDate == null) { return null; } cal.setTime(succDate); cal.add(Calendar.DAY_OF_YEAR, -1); succDate = cal.getTime(); numberOfWorkDays = getNumberOfWorkDaysBetweenDates(predDate, succDate); break; } //start, end date must be removed. numberOfWorkDays -= 1; return numberOfWorkDays; } /** * This method handle link lag changing, and creating. * For example: if a link lag increasing from 0 to 2 days the item and successors will be cascaded (if needed). * @param lagInDaysOrDiffOldLagNewLag * @param linkBean * @param pred * @param succ * @param personID * @param locale * @param isNew * @return */ static Set<Integer> handleLagAndCascade(int lagInDaysOrDiffOldLagNewLag, TWorkItemLinkBean linkBean, TWorkItemBean pred, TWorkItemBean succ, Integer personID, Locale locale, boolean isNew) { Set<Integer> violatedSuccessorDependencies = new HashSet<Integer>(); Set<Integer> violatedDescendantDependencies = new HashSet<Integer>(); int allLinkLag = lagInDaysOrDiffOldLagNewLag; if (!isNew) { allLinkLag = getLinkLagInDays(linkBean, pred.getObjectID()); } Date newStartDate = new Date(); Date newEndDate = new Date(); if (lagInDaysOrDiffOldLagNewLag > 0 || (lagInDaysOrDiffOldLagNewLag == 0 && isNew)) { Integer numberOfWorkdaysBetweenTasksBaseLinkType = getNumberOfDaysBetweenTaskBasedOnLinkType(linkBean, pred, succ); if (numberOfWorkdaysBetweenTasksBaseLinkType == null) { //start or end date not specified, allow to add link without restriction return violatedSuccessorDependencies; } int difference = allLinkLag - numberOfWorkdaysBetweenTasksBaseLinkType; if (difference > 0) { //increase in lag should not be a problem newStartDate = MsProjectLinkTypeBL.stepForward(getStartDate(succ), difference, true); newEndDate = MsProjectLinkTypeBL.stepForward(getEndDate(succ), difference, true); MsProjectLinkTypeBL.saveActualBean(succ, personID, locale, newStartDate, newEndDate); } } else if (lagInDaysOrDiffOldLagNewLag < 0) { //decrease should be verified in linked successors and child items newStartDate = MsProjectLinkTypeBL.stepBack(getStartDate(succ), Math.abs(lagInDaysOrDiffOldLagNewLag), true); newEndDate = MsProjectLinkTypeBL.stepBack(getEndDate(succ), Math.abs(lagInDaysOrDiffOldLagNewLag), true); violatedSuccessorDependencies = MsProjectLinkTypeBL.checkSuccMoveValidity(getStartDate(succ), getEndDate(succ), succ.getObjectID(), personID, linkBean); Map<Integer, WorkItemWithNewCascadedDates> itemCascadeMap = new HashMap<Integer, WorkItemWithNewCascadedDates>(); MsProjectLinkType msProjectLinkType = MsProjectLinkType.getInstance(); List<Integer> msProjectLinkTypeIDs = LinkTypeBL.getLinkTypesByPluginClass(msProjectLinkType); violatedDescendantDependencies = MsProjectLinkTypeBL.checkChildrens(succ.getObjectID(), succ, personID, getStartDate(succ), getEndDate(succ), newStartDate, newEndDate, locale, msProjectLinkTypeIDs, itemCascadeMap); Set<Integer> allProblems = new HashSet<Integer>(); allProblems.addAll(violatedSuccessorDependencies); allProblems.addAll(violatedDescendantDependencies); if (allProblems.size() > 0) { return allProblems; } MsProjectLinkTypeBL.saveActualBean(succ, personID, locale, newStartDate, newEndDate); } return violatedSuccessorDependencies; } public static Date getStartDate(TWorkItemBean bean) { boolean showBaseline = ApplicationBean.getInstance().getSiteBean().getShowBaseline(); if (bean != null) { if (showBaseline) { return bean.getTopDownStartDate(); } else { return bean.getStartDate(); } } else { return null; } } public static Date getEndDate(TWorkItemBean bean) { boolean showBaseline = ApplicationBean.getInstance().getSiteBean().getShowBaseline(); if (bean != null) { if (showBaseline) { return bean.getTopDownEndDate(); } else { return bean.getEndDate(); } } else { return null; } } public static void setStartDate(TWorkItemBean bean, Date newStartDate) { if (bean != null) { boolean showBaseline = ApplicationBean.getInstance().getSiteBean().getShowBaseline(); if (showBaseline) { bean.setTopDownStartDate(newStartDate); } else { bean.setStartDate(newStartDate); } } } public static void setEndDate(TWorkItemBean bean, Date newEndDate) { if (bean != null) { boolean showBaseline = ApplicationBean.getInstance().getSiteBean().getShowBaseline(); if (showBaseline) { bean.setTopDownEndDate(newEndDate); } else { bean.setEndDate(newEndDate); } } } public static void setPresentedFields(Set<Integer> presentField) { boolean showBaseline = ApplicationBean.getInstance().getSiteBean().getShowBaseline(); if (showBaseline) { presentField.add(SystemFields.TOP_DOWN_START_DATE); presentField.add(SystemFields.TOP_DOWN_END_DATE); } else { presentField.add(SystemFields.INTEGER_STARTDATE); presentField.add(SystemFields.INTEGER_ENDDATE); } } }