/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition 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; version 3 of the License.
//
// This community edition 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/.
//
/////////////////////////////////////////////////////////////////////////////
package org.projectforge.task;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.hibernate.Hibernate;
import org.projectforge.access.AccessDao;
import org.projectforge.access.GroupTaskAccessDO;
import org.projectforge.access.OperationType;
import org.projectforge.common.AbstractCache;
import org.projectforge.common.DateHelper;
import org.projectforge.common.NumberHelper;
import org.projectforge.core.InternalErrorException;
import org.projectforge.debug.StackTraceHolder;
import org.projectforge.fibu.AuftragDao;
import org.projectforge.fibu.AuftragsPositionVO;
import org.projectforge.fibu.ProjektDO;
import org.projectforge.fibu.ProjektDao;
import org.projectforge.fibu.kost.Kost2DO;
import org.projectforge.fibu.kost.KostCache;
import org.projectforge.registry.Registry;
import org.projectforge.timesheet.TimesheetDO;
import org.projectforge.timesheet.TimesheetDao;
/**
* Holds the complete task list in a tree. It will be initialized by the values read from the database. Any changes will be written to this
* tree and to the database.
* @author Kai Reinhard (k.reinhard@micromata.de)
*/
public class TaskTree extends AbstractCache implements Serializable
{
private static final long serialVersionUID = 3748005966442878168L;
@SpringBean(name = "taskDao")
private transient TaskDao taskDao;
@SpringBean(name = "accessDao")
private transient AccessDao accessDao;
@SpringBean(name = "kost2Dao")
private transient ProjektDao projektDao;
@SpringBean(name = "kostCache")
private transient KostCache kostCache;
@SpringBean(name = "auftragDao")
private transient AuftragDao auftragDao;
private static final List<TaskNode> EMPTY_LIST = new ArrayList<TaskNode>();
/** For log messages. */
private static final Logger log = Logger.getLogger(TaskTree.class);
/** Time of last modification in milliseconds from 1970-01-01. */
private long timeOfLastModification = 0;
/** For faster searching of entries. */
private Map<Integer, TaskNode> taskMap;
/** The root node of all tasks. The only node with parent null. */
private TaskNode root = null;
private Map<Integer, Set<AuftragsPositionVO>> orderPositionReferences;
private boolean orderPositionReferencesDirty = true;
public TaskNode getRootTaskNode()
{
checkRefresh();
return this.root;
}
/** Adds the given node as child of the given parent. */
private synchronized TaskNode addTaskNode(final TaskNode node, final TaskNode parent)
{
checkRefresh();
if (parent != null) {
node.setParent(parent);
parent.addChild(node);
}
updateTimeOfLastModification();
return node;
}
/**
* Adds a new node with the given data. The given Task holds all data and the information (id) of the parent node of the node to add. Will
* be called by TaskDAO after inserting a new task.
*/
TaskNode addTaskNode(final TaskDO task)
{
checkRefresh();
final TaskNode node = new TaskNode();
node.setTask(task);
final TaskNode parent = getTaskNodeById(task.getParentTaskId());
if (parent != null) {
node.setParent(parent);
} else if (root == null) {
// this is the root node:
root = node;
} else if (node.getId().equals(root.getId()) == false) {
// This node is not the root node:
node.setParent(root);
}
taskMap.put(node.getId(), node);
final TimesheetDao timesheetDao = Registry.instance().getDao(TimesheetDao.class);
final TimesheetDO timesheet = new TimesheetDO().setTask(task);
final boolean bookable = timesheetDao.checkTaskBookable(timesheet, null, OperationType.INSERT, false);
node.bookableForTimesheets = bookable;
return addTaskNode(node, parent);
}
/**
* @param taskId
* @param ancestorTaskId
* @return
* @see TaskNode#getPathToAncestor(Integer)
*/
public List<TaskNode> getPath(final Integer taskId, final Integer ancestorTaskId)
{
checkRefresh();
if (taskId == null) {
return EMPTY_LIST;
}
final TaskNode taskNode = getTaskNodeById(taskId);
if (taskNode == null) {
return EMPTY_LIST;
}
return taskNode.getPathToAncestor(ancestorTaskId);
}
/**
* Returns the path to the root node in an ArrayList.
* @see #getPath(Integer, Integer)
*/
public List<TaskNode> getPathToRoot(final Integer taskId)
{
return getPath(taskId, null);
}
/** All task nodes are stored in an HashMap for faster searching. */
public TaskNode getTaskNodeById(final Integer id)
{
if (id == null) {
return null;
}
checkRefresh();
return taskMap.get(id);
}
public TaskDO getTaskById(final Integer id)
{
checkRefresh();
final TaskNode node = getTaskNodeById(id);
if (node != null) {
return node.getTask();
}
return null;
}
/**
* Gets the project, which is assigned to the task or if not found to the parent task or grand parent task etc.
* @param taskId
* @return null, if now project is assigned to this task or ancestor tasks.
*/
public ProjektDO getProjekt(final Integer taskId)
{
if (taskId == null) {
return null;
}
final TaskNode node = getTaskNodeById(taskId);
if (node != null) {
return node.getProjekt();
} else {
return null;
}
}
public void internalSetProject(final Integer taskId, final ProjektDO projekt)
{
final TaskNode node = getTaskNodeById(taskId);
if (node == null) {
throw new InternalErrorException("Could not found task with id " + taskId + " in internalSetProject");
}
node.projekt = projekt;
}
/**
* recursive = true.
* @param taskId
* @return
* @see #getKost2List(Integer, boolean)
*/
public List<Kost2DO> getKost2List(final Integer taskId)
{
final TaskNode node = getTaskNodeById(taskId);
return getKost2List(node, true);
}
/**
* Get the available and active Kost2DOs of the task, or if not available of the first found ancestor tasks if available. Kost2 are
* defined over assigned projects and kost2s. If project or Kost2DO not assigned for a task, then the project or task of the parent task
* will be assumed. If the parent task has no project or task the grand parent task will be taken and so on (recursive until root task).
* @param taskId
* @param recursive If true then search the ancestor task for cost definitions if current task haven't.
* @return Available Kost2DOs or null, if no Kost2DO found.
*/
public List<Kost2DO> getKost2List(final Integer taskId, final boolean recursive)
{
final TaskNode node = getTaskNodeById(taskId);
return getKost2List(node, recursive);
}
/**
*
* @param projekt If not initialized then the project is get from the data base.
* @param task Only needed for output if an entry (Kost2) of the blackWhiteList cannot be found.
* @param blackWhiteList
* @param kost2IsBlackList
* @return
*/
public List<Kost2DO> getKost2List(ProjektDO projekt, final TaskDO task, final String[] blackWhiteList, final boolean kost2IsBlackList)
{
final List<Kost2DO> kost2List = new ArrayList<Kost2DO>();
final boolean wildcard = blackWhiteList != null && blackWhiteList.length == 1 && "*".equals(blackWhiteList[0]);
if (projekt != null && Hibernate.isPropertyInitialized(projekt, "kunde") == false) {
projekt = projektDao.internalGetById(projekt.getId());
}
if (projekt != null) {
final List<Kost2DO> list = kostCache.getActiveKost2(projekt.getNummernkreis(), projekt.getBereich(), projekt.getNummer());
if (CollectionUtils.isNotEmpty(list) == true) {
for (final Kost2DO kost2 : list) {
if (wildcard == true) { // black-white-list is "*".
if (kost2IsBlackList == true) {
break; // Do not add any entry.
} else {
kost2List.add(kost2); // Add all entries.
}
} else if (blackWhiteList == null || blackWhiteList.length == 0) {
// Add all (either black nor white entry is given):
kost2List.add(kost2);
} else {
final String no = kost2.getFormattedNumber();
boolean add = kost2IsBlackList; // false for white list and true for black list at default.
for (final String item : blackWhiteList) {
if (no.endsWith(item) == true) {
if (kost2IsBlackList == true) {
// Black list entry matches, so do not add entry:
add = false;
break;
} else {
// White list entry matches, so add entry:
add = true;
break;
}
}
}
if (add == true) {
kost2List.add(kost2);
}
}
}
}
} else if (kost2IsBlackList == false && blackWhiteList != null) {
// Add all given KoSt2DOs.
for (final String item : blackWhiteList) {
final Kost2DO kost2 = kostCache.getKost2(item);
if (kost2 != null) {
kost2List.add(kost2);
} else {
log.info("Given kost2 not found: '" + item + "'. Specified at task " + task.getId() + " - " + task);
}
}
}
if (CollectionUtils.isNotEmpty(kost2List) == true) {
Collections.sort(kost2List);
return kost2List;
} else {
return null;
}
}
private List<Kost2DO> getKost2List(final TaskNode node, final boolean recursive)
{
if (node == null) {
return null;
}
final TaskDO task = node.getTask();
final String[] blackWhiteList = task.getKost2BlackWhiteItems();
final ProjektDO projekt = node.getProjekt(blackWhiteList != null); // If black-white-list is null then do not search for projekt of
// ancestor tasks.
final List<Kost2DO> list = getKost2List(projekt, task, blackWhiteList, task.isKost2IsBlackList());
if (list != null) {
return list;
} else if (node.parent != null && recursive == true) {
return getKost2List(node.parent, recursive);
} else {
return null;
}
}
/**
* Should be called after modification of a time sheet assigned to the given task id.
* @param taskId
*/
public void resetTotalDuration(final Integer taskId)
{
final TaskNode node = getTaskNodeById(taskId);
if (node == null) {
log.error("Task id '" + taskId + "' not found.");
return;
}
node.totalDuration = -1;
}
/**
* After changing a task this method will be called by TaskDao for updating the task and the task tree.
* @param task Updating the existing task in the taskTree. If not exist, a new task will be added.
*/
TaskNode addOrUpdateTaskNode(final TaskDO task)
{
checkRefresh();
Validate.notNull(task);
Validate.notNull(task.getId());
final TaskNode node = getTaskNodeById(task.getId());
if (node == null) {
return addTaskNode(task);
}
node.setTask(task);
if (task.getParentTaskId() != null && task.getParentTaskId().equals(node.getParent().getId()) == false) {
if (log.isDebugEnabled() == true) {
log.debug("Task hierarchy was changed for task: " + task);
}
final TaskNode oldParent = node.getParent();
Validate.notNull(oldParent);
oldParent.removeChild(node);
final TaskNode newParent = getTaskNodeById(task.getParentTaskId());
node.setParent(newParent);
newParent.addChild(node);
}
updateTimeOfLastModification();
return node;
}
/**
* Sets an explicit task group access for the given task (stored in the given groupTaskAccess). This method will be called by AccessDao
* after inserting or updating GroupTaskAccess to the database.
* @see GroupTaskAccess
*/
public void setGroupTaskAccess(final GroupTaskAccessDO groupTaskAccess)
{
checkRefresh();
final Integer taskId = groupTaskAccess.getTaskId();
final TaskNode node = taskMap.get(taskId);
node.setGroupTaskAccess(groupTaskAccess);
}
/**
* Removes an explicit task group access for the given task (stored in the given groupTaskAccess). This method will be called by AccessDao
* after deleting GroupTaskAccess from the database.
* @see GroupTaskAccess
*/
public void removeGroupTaskAccess(final GroupTaskAccessDO groupTaskAccess)
{
checkRefresh();
final Integer taskId = groupTaskAccess.getTaskId();
final TaskNode node = taskMap.get(taskId);
node.removeGroupTaskAccess(groupTaskAccess.getGroupId());
}
public long getTimeOfLastModification()
{
return this.timeOfLastModification;
}
@Override
public String toString()
{
if (root == null) {
return "<empty/>";
}
final Document document = DocumentHelper.createDocument();
final Element root = document.addElement("root");
this.root.addXMLElement(root);
// Pretty print the document to System.out
final StringWriter sw = new StringWriter();
String result = "";
final XMLWriter writer = new XMLWriter(sw, OutputFormat.createPrettyPrint());
try {
writer.write(document);
result = sw.toString();
} catch (final IOException ex) {
log.error(ex.getMessage(), ex);
} finally {
try {
writer.close();
} catch (final IOException ex) {
log.error("Error while closing xml writer: " + ex.getMessage(), ex);
}
}
return result;
}
public TaskTree()
{
}
public void setTaskDao(final TaskDao taskDao)
{
this.taskDao = taskDao;
}
TaskDao getTaskDao()
{
return taskDao;
}
public void setGroupTaskAccessdao(final AccessDao accessDao)
{
this.accessDao = accessDao;
}
public void setProjektDao(final ProjektDao projektDao)
{
this.projektDao = projektDao;
}
public void setKostCache(final KostCache kostCache)
{
this.kostCache = kostCache;
}
public void setAuftragDao(final AuftragDao auftragDao)
{
this.auftragDao = auftragDao;
auftragDao.registerTaskTree(this);
}
/**
* Has the current logged in user select access to the given task?
* @param node
* @return
*/
public boolean hasSelectAccess(final TaskNode node)
{
return taskDao.hasLoggedInUserSelectAccess(node.getTask(), false);
}
/**
* @see #isRootNode(TaskDO)
*/
public boolean isRootNode(final TaskNode node)
{
Validate.notNull(node);
return isRootNode(node.getTask());
}
/**
* @param node
* @return true, if the given task has the same id as the task tree's root node, otherwise false;
*/
public boolean isRootNode(final TaskDO task)
{
Validate.notNull(task);
if (root == null && task.getParentTaskId() == null) {
// First task, so it should be the root node.
return true;
}
checkRefresh();
if (task.getId() == null) {
// Node has no id, so it can't be the root node.
return false;
}
return root.getId().equals(task.getId());
}
/**
* Should be called after manipulations of any order position if a task reference was changed. This method declares the reference map as
* dirty, therefore before the next usage the map will be rebuild from the database.
*/
public void refreshOrderPositionReferences()
{
synchronized (this) {
this.orderPositionReferencesDirty = true;
}
}
/**
* Does any order position entry with a task reference exist?
*/
public boolean hasOrderPositionsEntries()
{
checkRefresh();
return (MapUtils.isNotEmpty(getOrderPositionEntries()));
}
private Map<Integer, Set<AuftragsPositionVO>> getOrderPositionEntries()
{
synchronized (this) {
if (this.orderPositionReferencesDirty == true) {
this.orderPositionReferences = auftragDao.getTaskReferences();
if (this.orderPositionReferences != null) {
resetOrderPersonDays(this.root);
for (final Map.Entry<Integer, Set<AuftragsPositionVO>> entry : this.orderPositionReferences.entrySet()) {
final TaskNode node = getTaskNodeById(entry.getKey());
node.orderedPersonDays = null;
if (CollectionUtils.isNotEmpty(entry.getValue()) == true) {
for (final AuftragsPositionVO pos : entry.getValue()) {
if (pos.getPersonDays() == null) {
continue;
}
if (node.orderedPersonDays == null) {
node.orderedPersonDays = BigDecimal.ZERO;
}
node.orderedPersonDays = node.orderedPersonDays.add(pos.getPersonDays());
}
}
}
}
this.orderPositionReferencesDirty = false;
}
return this.orderPositionReferences;
}
}
private void resetOrderPersonDays(final TaskNode node)
{
node.orderedPersonDays = null;
if (node.hasChilds() == true) {
for (final TaskNode child : node.getChilds()) {
resetOrderPersonDays(child);
}
}
}
/**
* @param taskId
* @return Set of all order positions assigned to the given task.
*/
public Set<AuftragsPositionVO> getOrderPositionEntries(final Integer taskId)
{
checkRefresh();
return getOrderPositionEntries().get(taskId);
}
/**
* @return Set of all order positions assigned to the given task and any of the ancestor tasks.
* @param taskId
* @return
*/
public Set<AuftragsPositionVO> getOrderPositionsUpwards(final Integer taskId)
{
final Set<AuftragsPositionVO> set = new TreeSet<AuftragsPositionVO>();
addOrderPositionsUpwards(set, taskId);
return set;
}
private void addOrderPositionsUpwards(final Set<AuftragsPositionVO> set, final Integer taskId)
{
final Set<AuftragsPositionVO> set2 = getOrderPositionEntries(taskId);
if (CollectionUtils.isNotEmpty(set2) == true) {
set.addAll(set2);
}
final TaskDO task = getTaskById(taskId);
if (task != null && task.getParentTaskId() != null) {
addOrderPositionsUpwards(set, task.getParentTaskId());
}
}
/**
* @param taskId
* @param recursive if true also all descendant tasks will be searched for assigned order positions.
* @return
*/
public boolean hasOrderPositions(final Integer taskId, final boolean recursive)
{
if (taskId == null) { // For new tasks.
return false;
}
if (CollectionUtils.isNotEmpty(getOrderPositionEntries(taskId)) == true) {
return true;
}
if (recursive == true) {
final TaskNode node = getTaskNodeById(taskId);
if (node != null && node.hasChilds() == true) {
for (final TaskNode child : node.getChilds()) {
if (hasOrderPositions(child.getId(), recursive) == true) {
return true;
}
}
}
}
return false;
}
/**
* @param taskId
* @return True, if the given task has order positions or any ancestor task has an order position.
*/
public boolean hasOrderPositionsUpwards(final Integer taskId)
{
if (hasOrderPositions(taskId, false) == true) {
return true;
}
final TaskNode task = getTaskNodeById(taskId);
if (task != null && task.getParentId() != null) {
return hasOrderPositionsUpwards(task.getParentId());
}
return false;
}
/**
* @param taskId
* @see #getPersonDays(TaskNode)
*/
public BigDecimal getPersonDays(final Integer taskId)
{
final TaskNode node = getTaskNodeById(taskId);
return getPersonDays(node);
}
/**
* @param node
* @return The ordered person days or if not found the defined max hours. If both not found, the get the sum of all diect or null if both
* not found.
* @see #getOrderedPersonDays(TaskNode)
* @see TaskNode#getMaxHours()
*/
public BigDecimal getPersonDays(final TaskNode node)
{
checkRefresh();
if (node == null || node.isDeleted() == true) {
return null;
}
if (hasOrderPositions(node.getId(), true) == true) {
return getOrderedPersonDaysSum(node);
}
final Integer maxHours = node.getTask().getMaxHours();
if (maxHours != null) {
return new BigDecimal(maxHours).divide(DateHelper.HOURS_PER_WORKING_DAY, 2, BigDecimal.ROUND_HALF_UP);
}
if (node.hasChilds() == false) {
return null;
}
BigDecimal result = null;
for (final TaskNode child : node.getChilds()) {
final BigDecimal childPersonDays = getPersonDays(child);
if (childPersonDays != null) {
if (result == null) {
result = BigDecimal.ZERO;
}
result = result.add(childPersonDays);
}
}
return result;
}
/**
* @return The sum of all ordered person days. This method checks the given node and all sub-nodes for assigned order positions.
*/
public BigDecimal getOrderedPersonDaysSum(final TaskNode node)
{
BigDecimal personDays = null;
if (node.orderedPersonDays != null) {
personDays = node.orderedPersonDays;
}
if (node.hasChilds() == true) {
for (final TaskNode child : node.getChilds()) {
final BigDecimal childPersonDays = getOrderedPersonDaysSum(child);
if (childPersonDays != null) {
if (personDays == null) {
personDays = childPersonDays;
} else {
personDays = personDays.add(childPersonDays);
}
}
}
}
return personDays;
}
public TaskNode getPersonDaysNode(final TaskNode node)
{
if (node == null) {
return null;
}
if (node.orderedPersonDays != null) {
return node;
}
if (NumberHelper.greaterZero(node.getTask().getMaxHours()) == true) {
return node;
}
return getPersonDaysNode(node.getParent());
}
/**
* Reads the sum of all time sheet durations grouped by task id and set the total duration of found taskNodes.
*/
private void readTotalDurations()
{
final List<Object[]> list = taskDao.readTotalDurations();
for (final Object[] res : list) {
final Integer taskId = (Integer) res[1];
final TaskNode node = getTaskNodeById(taskId);
if (node == null) {
log.warn("Task not found: " + taskId);
} else {
if (res[0] instanceof Integer) {
node.totalDuration = (Integer) res[0];
} else {
node.totalDuration = (Long) res[0];
}
}
}
}
/**
* Reads the sum of all time sheet durations grouped by task id and set the total duration of found taskNodes.
*/
public void readTotalDuration(final Integer taskId)
{
final long duration = taskDao.readTotalDuration(taskId);
final TaskNode node = getTaskNodeById(taskId);
if (node == null) {
log.warn("Task not found: " + taskId);
} else {
node.totalDuration = duration;
}
}
/**
* Should only called by test suite!
*/
public void clear()
{
this.root = null;
this.setExpired();
}
/**
* All tasks from database will be read and cached into this TaskTree. Also all explicit group task access' will be read from database and
* will be cached in this tree (implicit access' will be created too).<br/>
* The generation of the task tree will be done manually, not by hibernate because the task hierarchy is very sensible. Manipulations of
* the task tree should be done carefully for single task nodes.
*
* @see org.projectforge.common.AbstractCache#refresh()
*/
@Override
protected void refresh()
{
log.info("Initializing task tree ...");
if (taskDao == null) {
log.info("Can't initialize task tree, taskDao isn't set yet (shouldn't occur):");
// Stack trace for debugging refresh() call without TaskDao (does only occur in productive mode):
final StackTraceHolder sth = new StackTraceHolder();
log.info(sth);
return;
}
TaskNode newRoot = null;
taskMap = new HashMap<Integer, TaskNode>();
final List<TaskDO> taskList = taskDao.internalLoadAll();
TaskNode node;
log.debug("Loading list of tasks ...");
for (final TaskDO task : taskList) {
node = new TaskNode();
node.setTask(task);
taskMap.put(node.getTaskId(), node);
if (node.isRootNode() == true) {
if (newRoot != null) {
log.error("Duplicate root node found: " + newRoot.getId() + " and " + node.getId());
node.setParent(newRoot); // Set the second root task as child task of first read root task.
} else {
if (log.isDebugEnabled() == true) {
log.debug("Root note found: " + node);
}
newRoot = node;
}
}
}
if (newRoot == null) {
log.fatal("OUPS, no task found (ProjectForge database not initialized?) OK, initialize it ...");
final TaskDO rootTask = new TaskDO();
rootTask.setTitle("root");
rootTask.setShortDescription("ProjectForge root task");
taskDao.internalSave(rootTask);
newRoot = new TaskNode();
newRoot.setTask(rootTask);
taskMap.put(newRoot.getTaskId(), newRoot);
}
this.root = newRoot;
if (log.isDebugEnabled() == true) {
log.debug("Creating tree for " + taskList.size() + " tasks ...");
}
for (final TaskDO task : taskList) {
TaskNode parentNode = null;
node = taskMap.get(task.getId());
final Integer parentId = task.getParentTaskId();
if (parentId != null) {
parentNode = taskMap.get(parentId);
}
// log.debug("Processing node: " + node.getId() + ", parent: " + parentId);
if (parentNode != null) {
node.setParent(parentNode);
parentNode.addChild(node);
updateTimeOfLastModification();
} else {
log.debug("Processing root node:" + node);
}
}
if (log.isDebugEnabled() == true) {
log.debug(this.root);
}
// Now read all explicit group task access' from the database:
final List<GroupTaskAccessDO> accessList = accessDao.internalLoadAll();
for (final GroupTaskAccessDO access : accessList) {
node = taskMap.get(access.getTaskId());
node.setGroupTaskAccess(access);
if (log.isDebugEnabled() == true) {
log.debug(access.toString());
}
}
// Now read all projects with their references to tasks:
final List<ProjektDO> projects = projektDao.internalLoadAll();
if (projects != null) {
for (final ProjektDO project : projects) {
if (project.isDeleted() == true || project.getTaskId() == null) {
continue;
}
node = taskMap.get(project.getTaskId());
if (node == null) {
log.error("Oups, should not occur: project references a non existing task: " + project);
} else {
node.projekt = project;
}
}
}
if (log.isDebugEnabled() == true) {
log.debug(this.toString());
}
readTotalDurations();
refreshOrderPositionReferences();
// Now update the status: bookable for time sheets:
final TimesheetDao timesheetDao = Registry.instance().getDao(TimesheetDao.class);
final TimesheetDO timesheet = new TimesheetDO();
for (final TaskDO task : taskList) {
node = taskMap.get(task.getId());
timesheet.setTask(task);
final boolean bookable = timesheetDao.checkTaskBookable(timesheet, null, OperationType.INSERT, false);
node.bookableForTimesheets = bookable;
}
log.info("Initializing task tree done.");
}
private void updateTimeOfLastModification()
{
this.timeOfLastModification = new Date().getTime();
}
}