All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.mpxj.asta.AstaReader Maven / Gradle / Ivy

/*
 * file:       AstaReader.java
 * author:     Jon Iles
 * copyright:  (c) Packwood Software 2011
 * date:       07/04/2011
 */

/*
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * This library 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 Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */

package net.sf.mpxj.asta;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import net.sf.mpxj.ChildTaskContainer;
import net.sf.mpxj.ConstraintType;
import net.sf.mpxj.DateRange;
import net.sf.mpxj.Day;
import net.sf.mpxj.DayType;
import net.sf.mpxj.Duration;
import net.sf.mpxj.EventManager;
import net.sf.mpxj.ProjectCalendar;
import net.sf.mpxj.ProjectCalendarHours;
import net.sf.mpxj.ProjectCalendarWeek;
import net.sf.mpxj.ProjectConfig;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.ProjectProperties;
import net.sf.mpxj.RelationType;
import net.sf.mpxj.Resource;
import net.sf.mpxj.ResourceAssignment;
import net.sf.mpxj.ResourceType;
import net.sf.mpxj.Task;
import net.sf.mpxj.TimeUnit;
import net.sf.mpxj.common.DateHelper;
import net.sf.mpxj.common.NumberHelper;

/**
 * This class provides a generic front end to read project data from
 * a database.
 */
final class AstaReader
{
   /**
    * Constructor.
    */
   public AstaReader()
   {
      m_project = new ProjectFile();
      m_eventManager = m_project.getEventManager();

      ProjectConfig config = m_project.getProjectConfig();

      config.setAutoTaskUniqueID(false);
      config.setAutoResourceUniqueID(false);

      config.setAutoCalendarUniqueID(false);

      m_project.getProjectProperties().setFileApplication("Asta");
      m_project.getProjectProperties().setFileType("PP");
   }

   /**
    * Retrieves the project data read from this file.
    *
    * @return project data
    */
   public ProjectFile getProject()
   {
      return m_project;
   }

   /**
    * Process project properties.
    *
    * @param row project properties data.
    */
   public void processProjectProperties(Row row)
   {
      ProjectProperties ph = m_project.getProjectProperties();
      ph.setDuration(row.getDuration("DURATIONHOURS"));
      ph.setStartDate(row.getDate("STARU"));
      ph.setFinishDate(row.getDate("ENE"));
      ph.setName(row.getString("SHORT_NAME"));
      ph.setAuthor(row.getString("PROJECT_BY"));
      //DURATION_TIME_UNIT
      ph.setLastSaved(row.getDate("LAST_EDITED_DATE"));
   }

   /**
    * Process resources.
    *
    * @param permanentRows permanent resource data
    * @param consumableRows consumable resource data
    */
   public void processResources(List permanentRows, List consumableRows)
   {
      //
      // Process permanent resources
      //
      for (Row row : permanentRows)
      {
         Resource resource = m_project.addResource();
         resource.setType(ResourceType.WORK);
         resource.setUniqueID(row.getInteger("PERMANENT_RESOURCEID"));
         resource.setEmailAddress(row.getString("EMAIL_ADDRESS"));
         // EFFORT_TIME_UNIT
         resource.setName(row.getString("NASE"));
         resource.setResourceCalendar(deriveResourceCalendar(row.getInteger("CALENDAV")));
         resource.setMaxUnits(Double.valueOf(row.getDouble("AVAILABILITY").doubleValue() * 100));
         resource.setIsGeneric(row.getBoolean("CREATED_AS_FOLDER"));
         resource.setInitials(getInitials(resource.getName()));
      }

      //
      // Process groups
      //
      /*
            for (Row row : permanentRows)
            {
               Resource resource = m_project.getResourceByUniqueID(row.getInteger("PERMANENT_RESOURCEID"));
               Resource group = m_project.getResourceByUniqueID(row.getInteger("ROLE"));
               if (resource != null && group != null)
               {
                  resource.setGroup(group.getName());
               }
            }
      */
      //
      // Process consumable resources
      //
      for (Row row : consumableRows)
      {
         Resource resource = m_project.addResource();
         resource.setType(ResourceType.MATERIAL);
         resource.setUniqueID(row.getInteger("CONSUMABLE_RESOURCEID"));
         resource.setCostPerUse(row.getDouble("COST_PER_USEDEFAULTSAMOUNT"));
         resource.setPeakUnits(Double.valueOf(row.getDouble("AVAILABILITY").doubleValue() * 100));
         resource.setName(row.getString("NASE"));
         resource.setResourceCalendar(deriveResourceCalendar(row.getInteger("CALENDAV")));
         resource.setAvailableFrom(row.getDate("AVAILABLE_FROM"));
         resource.setAvailableTo(row.getDate("AVAILABLE_TO"));
         resource.setIsGeneric(row.getBoolean("CREATED_AS_FOLDER"));
         resource.setMaterialLabel(row.getString("MEASUREMENT"));
         resource.setInitials(getInitials(resource.getName()));
      }
   }

   /**
    * Derive a calendar for a resource.
    *
    * @param parentCalendarID calendar from which resource calendar is derived
    * @return new calendar for a resource
    */
   private ProjectCalendar deriveResourceCalendar(Integer parentCalendarID)
   {
      ProjectCalendar calendar = m_project.addDefaultDerivedCalendar();
      calendar.setUniqueID(Integer.valueOf(m_project.getProjectConfig().getNextCalendarUniqueID()));
      calendar.setParent(m_project.getCalendarByUniqueID(parentCalendarID));
      return calendar;
   }

   /**
    * Organises the data from Asta into a hierarchy and converts this into tasks.
    *
    * @param bars bar data
    * @param expandedTasks expanded task data
    * @param tasks task data
    * @param milestones milestone data
    */
   public void processTasks(List bars, List expandedTasks, List tasks, List milestones)
   {
      List parentBars = buildRowHierarchy(bars, expandedTasks, tasks, milestones);
      createTasks(m_project, "", parentBars);
      deriveProjectCalendar();
      updateStructure();
   }

   /**
    * Builds the task hierarchy.
    *
    * Note that there are two distinct levels of organisation going on here. The first is the
    * Asta "summary" organisation, where the user organises bars into summary groups. We are using this
    * to create our hierarchy of tasks.
    *
    * The second level displayed within a summary group (or at the project level if the user has not
    * created summary groups) is the WBS. At the moment we are not including the WBS in the hierarchy.
    *
    * @param bars bar data
    * @param expandedTasks expanded task data
    * @param tasks task data
    * @param milestones milestone data
    * @return list containing the top level tasks
    */
   private List buildRowHierarchy(List bars, List expandedTasks, List tasks, List milestones)
   {
      //
      // Create a list of leaf nodes by merging the task and milestone lists
      //
      List leaves = new ArrayList();
      leaves.addAll(tasks);
      leaves.addAll(milestones);

      //
      // Sort the bars and the leaves
      //
      Collections.sort(bars, BAR_COMPARATOR);
      Collections.sort(leaves, LEAF_COMPARATOR);

      //
      // Map bar IDs to bars
      //
      Map barIdToBarMap = new HashMap();
      for (Row bar : bars)
      {
         barIdToBarMap.put(bar.getInteger("BARID"), bar);
      }

      //
      // Merge expanded task attributes with parent bars
      // and create an expanded task ID to bar map.
      //
      Map expandedTaskIdToBarMap = new HashMap();
      for (Row expandedTask : expandedTasks)
      {
         Row bar = barIdToBarMap.get(expandedTask.getInteger("BAR"));
         bar.merge(expandedTask, "_");
         Integer expandedTaskID = bar.getInteger("_EXPANDED_TASKID");
         expandedTaskIdToBarMap.put(expandedTaskID, bar);
      }

      //
      // Build the hierarchy
      //
      List parentBars = new ArrayList();
      for (Row bar : bars)
      {
         Integer expandedTaskID = bar.getInteger("EXPANDED_TASK");
         Row parentBar = expandedTaskIdToBarMap.get(expandedTaskID);
         if (parentBar == null)
         {
            parentBars.add(bar);
         }
         else
         {
            parentBar.addChild(bar);
         }
      }

      //
      // Attach the leaves
      //
      for (Row leaf : leaves)
      {
         Integer barID = leaf.getInteger("BAR");
         Row bar = barIdToBarMap.get(barID);
         bar.addChild(leaf);
      }

      //
      // Prune any "displaced items" from the top level.
      // We're using a heuristic here as this is the only thing I
      // can see which differs between bars that we want to include
      // and bars that we want to exclude.
      //
      Iterator iter = parentBars.iterator();
      while (iter.hasNext())
      {
         Row bar = iter.next();
         if (bar.getDate("LAST_EDITED_DATE") == null)
         {
            iter.remove();
         }
      }

      //
      // If we only have a single top level node (effectively a summary task) prune that too.
      //
      if (parentBars.size() == 1)
      {
         parentBars = parentBars.get(0).getChildRows();
      }

      return parentBars;
   }

   /**
    * Recursively descend through  the hierarchy creating tasks.
    *
    * @param parent parent task
    * @param parentName parent name
    * @param rows rows to add as tasks to this parent
    */
   private void createTasks(ChildTaskContainer parent, String parentName, List rows)
   {
      for (Row row : rows)
      {
         Task task = parent.addTask();

         //
         // Do we have a bar, task, or milestone?
         //
         if (row.getInteger("BARID") != null)
         {
            //
            // If the bar only has one child task, we skip it and add the task directly
            //
            if (skipBar(row))
            {
               populateLeaf(row.getString("NAMH"), row.getChildRows().get(0), task);
            }
            else
            {
               populateBar(row, task);
               createTasks(task, task.getName(), row.getChildRows());
            }
         }
         else
         {
            populateLeaf(parentName, row, task);
         }

         m_eventManager.fireTaskReadEvent(task);
      }
   }

   /**
    * Returns true if we should skip this bar, i.e. the bar only has a single child task.
    *
    * @param row bar row to test
    * @return true if this bar should be skipped
    */
   private boolean skipBar(Row row)
   {
      List childRows = row.getChildRows();
      return childRows.size() == 1 && childRows.get(0).getChildRows().isEmpty();
   }

   /**
    * Adds a leaf node, which could be a task or a milestone.
    *
    * @param parentName parent bar name
    * @param row row to add
    * @param task task to populate with data from the row
    */
   private void populateLeaf(String parentName, Row row, Task task)
   {
      if (row.getInteger("TASKID") != null)
      {
         populateTask(row, task);
      }
      else
      {
         populateMilestone(row, task);
      }

      String name = task.getName();
      if (name == null || name.isEmpty())
      {
         task.setName(parentName);
      }
   }

   /**
    * Populate a task from a Row instance.
    *
    * @param row Row instance
    * @param task Task instance
    */
   private void populateTask(Row row, Task task)
   {
      //"PROJID"
      task.setUniqueID(row.getInteger("TASKID"));
      //GIVEN_DURATIONTYPF
      //GIVEN_DURATIONELA_MONTHS
      task.setDuration(row.getDuration("GIVEN_DURATIONHOURS"));
      task.setResume(row.getDate("RESUME"));
      //task.setStart(row.getDate("GIVEN_START"));
      //LATEST_PROGRESS_PERIOD
      //TASK_WORK_RATE_TIME_UNIT
      //TASK_WORK_RATE
      //PLACEMENT
      //BEEN_SPLIT
      //INTERRUPTIBLE
      //HOLDING_PIN
      ///ACTUAL_DURATIONTYPF
      //ACTUAL_DURATIONELA_MONTHS
      task.setActualDuration(row.getDuration("ACTUAL_DURATIONHOURS"));
      task.setEarlyStart(row.getDate("EARLY_START_DATE"));
      task.setLateStart(row.getDate("LATE_START_DATE"));
      //FREE_START_DATE
      //START_CONSTRAINT_DATE
      //END_CONSTRAINT_DATE
      //task.setBaselineWork(row.getDuration("EFFORT_BUDGET"));
      //NATURAO_ORDER
      //LOGICAL_PRECEDENCE
      //SPAVE_INTEGER
      //SWIM_LANE
      //USER_PERCENT_COMPLETE
      task.setPercentageComplete(row.getDouble("OVERALL_PERCENV_COMPLETE"));
      //OVERALL_PERCENT_COMPL_WEIGHT
      task.setName(row.getString("NARE"));
      task.setNotes(getNotes(row));
      //UNIQUE_TASK_ID
      task.setCalendar(m_project.getCalendarByUniqueID(row.getInteger("CALENDAU")));
      //EFFORT_TIMI_UNIT
      //WORL_UNIT
      //LATEST_ALLOC_PROGRESS_PERIOD
      //WORN
      //BAR
      //CONSTRAINU
      //PRIORITB
      //CRITICAM
      //USE_PARENU_CALENDAR
      //BUFFER_TASK
      //MARK_FOS_HIDING
      //OWNED_BY_TIMESHEEV_X
      //START_ON_NEX_DAY
      //LONGEST_PATH
      //DURATIOTTYPF
      //DURATIOTELA_MONTHS
      //DURATIOTHOURS
      task.setStart(row.getDate("STARZ"));
      task.setFinish(row.getDate("ENJ"));
      //DURATION_TIMJ_UNIT
      //UNSCHEDULABLG
      //SUBPROJECT_ID
      //ALT_ID
      //LAST_EDITED_DATE
      //LAST_EDITED_BY

      processConstraints(row, task);

      if (NumberHelper.getInt(task.getPercentageComplete()) != 0)
      {
         task.setActualStart(task.getStart());
         if (task.getPercentageComplete().intValue() == 100)
         {
            task.setActualFinish(task.getFinish());
            task.setDuration(task.getActualDuration());
         }
      }
   }

   /**
    * Uses data from a bar to populate a task.
    *
    * @param row bar data
    * @param task task to populate
    */
   private void populateBar(Row row, Task task)
   {
      Integer calendarID = row.getInteger("CALENDAU");
      ProjectCalendar calendar = m_project.getCalendarByUniqueID(calendarID);

      //PROJID
      task.setUniqueID(row.getInteger("BARID"));
      task.setStart(row.getDate("STARV"));
      task.setFinish(row.getDate("ENF"));
      //NATURAL_ORDER
      //SPARI_INTEGER
      task.setName(row.getString("NAMH"));
      //EXPANDED_TASK
      //PRIORITY
      //UNSCHEDULABLE
      //MARK_FOR_HIDING
      //TASKS_MAY_OVERLAP
      //SUBPROJECT_ID
      //ALT_ID
      //LAST_EDITED_DATE
      //LAST_EDITED_BY
      //Proc_Approve
      //Proc_Design_info
      //Proc_Proc_Dur
      //Proc_Procurement
      //Proc_SC_design
      //Proc_Select_SC
      //Proc_Tender
      //QA Checked
      //Related_Documents
      task.setCalendar(calendar);
   }

   /**
    * Populate a milestone from a Row instance.
    *
    * @param row Row instance
    * @param task Task instance
    */
   private void populateMilestone(Row row, Task task)
   {
      task.setMilestone(true);
      //PROJID
      task.setUniqueID(row.getInteger("MILESTONEID"));
      task.setStart(row.getDate("GIVEN_DATE_TIME"));
      task.setFinish(row.getDate("GIVEN_DATE_TIME"));
      //PROGREST_PERIOD
      //SYMBOL_APPEARANCE
      //MILESTONE_TYPE
      //PLACEMENU
      task.setPercentageComplete(row.getBoolean("COMPLETED") ? COMPLETE : INCOMPLETE);
      //INTERRUPTIBLE_X
      //ACTUAL_DURATIONTYPF
      //ACTUAL_DURATIONELA_MONTHS
      //ACTUAL_DURATIONHOURS
      task.setEarlyStart(row.getDate("EARLY_START_DATE"));
      task.setLateStart(row.getDate("LATE_START_DATE"));
      //FREE_START_DATE
      //START_CONSTRAINT_DATE
      //END_CONSTRAINT_DATE
      //EFFORT_BUDGET
      //NATURAO_ORDER
      //LOGICAL_PRECEDENCE
      //SPAVE_INTEGER
      //SWIM_LANE
      //USER_PERCENT_COMPLETE
      //OVERALL_PERCENV_COMPLETE
      //OVERALL_PERCENT_COMPL_WEIGHT
      task.setName(row.getString("NARE"));
      //NOTET
      //UNIQUE_TASK_ID
      task.setCalendar(m_project.getCalendarByUniqueID(row.getInteger("CALENDAU")));
      //EFFORT_TIMI_UNIT
      //WORL_UNIT
      //LATEST_ALLOC_PROGRESS_PERIOD
      //WORN
      //CONSTRAINU
      //PRIORITB
      //CRITICAM
      //USE_PARENU_CALENDAR
      //BUFFER_TASK
      //MARK_FOS_HIDING
      //OWNED_BY_TIMESHEEV_X
      //START_ON_NEX_DAY
      //LONGEST_PATH
      //DURATIOTTYPF
      //DURATIOTELA_MONTHS
      //DURATIOTHOURS
      //STARZ
      //ENJ
      //DURATION_TIMJ_UNIT
      //UNSCHEDULABLG
      //SUBPROJECT_ID
      //ALT_ID
      //LAST_EDITED_DATE
      //LAST_EDITED_BY
      task.setDuration(ZERO_HOURS);

   }

   /**
    * Iterates through the tasks setting the correct
    * outline level and ID values.
    */
   private void updateStructure()
   {
      int id = 1;
      Integer outlineLevel = Integer.valueOf(1);
      for (Task task : m_project.getChildTasks())
      {
         id = updateStructure(id, task, outlineLevel);
      }
   }

   /**
    * Iterates through the tasks setting the correct
    * outline level and ID values.
    *
    * @param id current ID value
    * @param task current task
    * @param outlineLevel current outline level
    * @return next ID value
    */
   private int updateStructure(int id, Task task, Integer outlineLevel)
   {
      task.setID(Integer.valueOf(id++));
      task.setOutlineLevel(outlineLevel);
      outlineLevel = Integer.valueOf(outlineLevel.intValue() + 1);
      for (Task childTask : task.getChildTasks())
      {
         id = updateStructure(id, childTask, outlineLevel);
      }
      return id;
   }

   /**
    * Processes predecessor data.
    *
    * @param rows predecessor data
    */
   public void processPredecessors(List rows)
   {
      for (Row row : rows)
      {
         Task startTask = m_project.getTaskByUniqueID(row.getInteger("START_TASK"));
         Task endTask = m_project.getTaskByUniqueID(row.getInteger("END_TASK"));

         if (startTask != null && endTask != null)
         {
            RelationType type = getRelationType(row.getInt("TYPI"));

            Duration startLag = row.getDuration("START_LAG_TIMEHOURS");
            Duration endLag = row.getDuration("END_LAG_TIMEHOURS");
            Duration lag = null;
            if (startLag.getDuration() != 0)
            {
               lag = startLag;
            }
            else
            {
               if (endLag.getDuration() != 0)
               {
                  lag = endLag;
               }
            }

            endTask.addPredecessor(startTask, type, lag);
         }

         //PROJID
         //LINKID
         //START_LAG_TIMETYPF
         //START_LAG_TIMEELA_MONTHS
         //START_LAG_TIMEHOURS
         //END_LAG_TIMETYPF
         //END_LAG_TIMEELA_MONTHS
         //END_LAG_TIMEHOURS
         //MAXIMUM_LAGTYPF
         //MAXIMUM_LAGELA_MONTHS
         //MAXIMUM_LAGHOURS
         //STARV_DATE
         //ENF_DATE
         //CURVATURE_PERCENTAGE
         //START_LAG_PERCENT_FLOAT
         //END_LAG_PERCENT_FLOAT
         //COMMENTS
         //LINK_CATEGORY
         //START_LAG_TIME_UNIT
         //END_LAG_TIME_UNIT
         //MAXIMUM_LAG_TIME_UNIT
         //START_TASK
         //END_TASK
         //TYPI
         //START_LAG_TYPE
         //END_LAG_TYPE
         //MAINTAIN_TASK_OFFSETS
         //UNSCHEDULABLF
         //CRITICAL
         //ON_LOOP
         //MAXIMUM_LAG_MODE
         //ANNOTATE_LEAD_LAG
         //START_REPOSITION_ON_TAS_MOVE
         //END_REPOSITION_ON_TASK_MOVE
         //DRAW_CURVED_IF_VERTICAL
         //AUTOMATIC_CURVED_LI_SETTINGS
         //DRAW_CURVED_LINK_TO_LEFT
         //LOCAL_LINK
         //DRIVING
         //ALT_ID
         //LAST_EDITED_DATE
         //LAST_EDITED_BY
      }
   }

   /**
    * Process assignment data.
    *
    * @param permanentAssignments assignment data
    */
   public void processAssignments(List permanentAssignments)
   {
      for (Row row : permanentAssignments)
      {
         Task task = m_project.getTaskByUniqueID(row.getInteger("ALLOCATEE_TO"));
         Resource resource = m_project.getResourceByUniqueID(row.getInteger("PLAYER"));
         if (task != null && resource != null)
         {
            double percentComplete = row.getDouble("PERCENT_COMPLETE").doubleValue();
            Duration work = row.getWork("EFFORW");
            double actualWork = work.getDuration() * percentComplete;
            double remainingWork = work.getDuration() - actualWork;

            ResourceAssignment assignment = task.addResourceAssignment(resource);
            assignment.setUniqueID(row.getInteger("PERMANENT_SCHEDUL_ALLOCATIONID"));
            assignment.setStart(row.getDate("STARZ"));
            assignment.setFinish(row.getDate("ENJ"));
            assignment.setUnits(Double.valueOf(row.getDouble("GIVEN_ALLOCATION").doubleValue() * 100));
            assignment.setDelay(row.getDuration("DELAAHOURS"));
            assignment.setPercentageWorkComplete(Double.valueOf(percentComplete * 100));
            assignment.setWork(work);
            assignment.setActualWork(Duration.getInstance(actualWork, work.getUnits()));
            assignment.setRemainingWork(Duration.getInstance(remainingWork, work.getUnits()));

         }

         //PROJID
         //REQUIREE_BY
         //OWNED_BY_TIMESHEET_X
         //EFFORW
         //GIVEN_EFFORT
         //WORK_FROM_TASK_FACTOR
         //ALLOCATIOO
         //GIVEN_ALLOCATION
         //ALLOCATIOP_OF
         //WORM_UNIT
         //WORK_RATE_TIMF_UNIT
         //EFFORT_TIMJ_UNIT
         //WORO
         //GIVEN_WORK
         //WORL_RATE
         //GIVEN_WORK_RATE
         //TYPV
         //CALCULATEG_PARAMETER
         //BALANCINJ_PARAMETER
         //SHAREE_EFFORT
         //CONTRIBUTES_TO_ACTIVI_EFFORT
         //DELAATYPF
         //DELAAELA_MONTHS
         //DELAAHOURS
         //GIVEO_DURATIONTYPF
         //GIVEO_DURATIONELA_MONTHS
         //GIVEO_DURATIONHOURS
         //DELAY_TIMI_UNIT
         //RATE_TYPE
         //USE_TASM_CALENDAR
         //IGNORF
         //ELAPSEE
         //MAY_BE_SHORTER_THAN_TASK
         //RESUMF
         //SPAXE_INTEGER
         //USER_PERCENU_COMPLETE
         //ALLOCATIOR_GROUP
         //PRIORITC
         //ACCOUNTED_FOR_ELSEWHERE
         //DURATIOTTYPF
         //DURATIOTELA_MONTHS
         //DURATIOTHOURS
         //DURATION_TIMJ_UNIT
         //UNSCHEDULABLG
         //SUBPROJECT_ID
         //permanent_schedul_allocation_ALT_ID
         //permanent_schedul_allocation_LAST_EDITED_DATE
         //permanent_schedul_allocation_LAST_EDITED_BY
         //perm_resource_skill_PROJID
         //PERM_RESOURCE_SKILLID
         //ARR_STOUT_STSKI_APARROW_TYPE
         //ARR_STOUT_STSKI_APLENGTH
         //ARR_STOUT_STSKI_APEDGE
         //ARR_STOUT_STSKI_APBORDET_COL
         //ARR_STOUT_STSKI_APINSIDG_COL
         //ARR_STOUT_STSKI_APPLACEMENW
         //BLI_STOUT_STSKI_APBLIP_TYPE
         //BLI_STOUT_STSKI_APSCALEY
         //BLI_STOUT_STSKI_APSCALEZ
         //BLI_STOUT_STSKI_APGAP
         //BLI_STOUT_STSKI_APBORDES_COL
         //BLI_STOUT_STSKI_APINSIDF_COL
         //BLI_STOUT_STSKI_APPLACEMENV
         //LIN_STOUT_STSKI_APSCALEX
         //LIN_STOUT_STSKI_APWIDTH
         //LIN_STOUT_STSKI_APBORDER_COL
         //LIN_STOUT_STSKI_APINSIDE_COL
         //LIN_STOUT_STSKI_APLINE_TYPE
         //SKI_APFOREGROUND_FILL_COLOUR
         //SKI_APBACKGROUND_FILL_COLOUR
         //SKI_APPATTERN
         //DURATIOODEFAULTTTYPF
         //DURATIOODEFAULTTELA_MONTHS
         //DURATIOODEFAULTTHOURS
         //DELAYDEFAULTTTYPF
         //DELAYDEFAULTTELA_MONTHS
         //DELAYDEFAULTTHOURS
         //DEFAULTTALLOCATION
         //DEFAULTTWORK_FROM_ACT_FACTOR
         //DEFAULTTEFFORT
         //DEFAULTTWORL
         //DEFAULTTWORK_RATE
         //DEFAULTTWORK_UNIT
         //DEFAULTTWORK_RATE_TIME_UNIT
         //DEFAULTTEFFORT_TIMG_UNIT
         //DEFAULTTDURATION_TIMF_UNIT
         //DEFAULTTDELAY_TIME_UNIT
         //DEFAULTTTYPL
         //DEFAULTTCALCULATED_PARAMETER
         //DEFAULTTBALANCING_PARAMETER
         //DEFAULTTWORK_RATE_TYPE
         //DEFAULTTUSE_TASK_CALENDAR
         //DEFAULTTALLOC_PROPORTIONALLY
         //DEFAULTTCAN_BE_SPLIT
         //DEFAULTTCAN_BE_DELAYED
         //DEFAULTTCAN_BE_STRETCHED
         //DEFAULTTACCOUNTED__ELSEWHERE
         //DEFAULTTCONTRIBUTES_T_EFFORT
         //DEFAULTTMAY_BE_SHORTER__TASK
         //DEFAULTTSHARED_EFFORT
         //ABILITY
         //EFFECTIVENESS
         //AVAILABLF_FROM
         //AVAILABLF_TO
         //SPARO_INTEGER
         //EFFORT_TIMF_UNIT
         //ROLE
         //CREATED_AS_FOLDER
         //perm_resource_skill_ALT_ID
         //perm_resource_skill_LAST_EDITED_DATE
         //perm_resource_skill_LAST_EDITED_BY

      }
   }

   /**
    * Convert an integer into a RelationType instance.
    *
    * @param index integer value
    * @return RelationType instance
    */
   private RelationType getRelationType(int index)
   {
      if (index < 0 || index > RELATION_TYPES.length)
      {
         index = 0;
      }

      return RELATION_TYPES[index];
   }

   /**
    * Convert a name into initials.
    *
    * @param name source name
    * @return initials
    */
   private String getInitials(String name)
   {
      String result = null;

      if (name != null && name.length() != 0)
      {
         StringBuilder sb = new StringBuilder();
         sb.append(name.charAt(0));
         int index = 1;
         while (true)
         {
            index = name.indexOf(' ', index);
            if (index == -1)
            {
               break;
            }

            ++index;
            if (index < name.length() && name.charAt(index) != ' ')
            {
               sb.append(name.charAt(index));
            }

            ++index;
         }

         result = sb.toString();
      }

      return result;
   }

   /**
    * Asta Powerproject assigns an explicit calendar for each task. This method
    * is used to find the most common calendar and use this as the default project
    * calendar. This allows the explicitly assigned task calendars to be removed.
    */
   private void deriveProjectCalendar()
   {
      //
      // Count the number of times each calendar is used
      //
      Map map = new HashMap();
      for (Task task : m_project.getAllTasks())
      {
         ProjectCalendar calendar = task.getCalendar();
         Integer count = map.get(calendar);
         if (count == null)
         {
            count = Integer.valueOf(1);
         }
         else
         {
            count = Integer.valueOf(count.intValue() + 1);
         }
         map.put(calendar, count);
      }

      //
      // Find the most frequently used calendar
      //
      int maxCount = 0;
      ProjectCalendar defaultCalendar = null;

      for (Entry entry : map.entrySet())
      {
         if (entry.getValue().intValue() > maxCount)
         {
            maxCount = entry.getValue().intValue();
            defaultCalendar = entry.getKey();
         }
      }

      //
      // Set the default calendar for the project
      // and remove it's use as a task-specific calendar.
      //
      if (defaultCalendar != null)
      {
         m_project.setDefaultCalendar(defaultCalendar);
         for (Task task : m_project.getAllTasks())
         {
            if (task.getCalendar() == defaultCalendar)
            {
               task.setCalendar(null);
            }
         }
      }
   }

   /**
    * Determines the constraints relating to a task.
    *
    * @param row row data
    * @param task Task instance
    */
   private void processConstraints(Row row, Task task)
   {
      ConstraintType constraintType = ConstraintType.AS_SOON_AS_POSSIBLE;
      Date constraintDate = null;

      switch (row.getInt("CONSTRAINU"))
      {
         case 0:
         {
            if (row.getInt("PLACEMENT") == 0)
            {
               constraintType = ConstraintType.AS_SOON_AS_POSSIBLE;
            }
            else
            {
               constraintType = ConstraintType.AS_LATE_AS_POSSIBLE;
            }
            break;
         }

         case 1:
         {
            constraintType = ConstraintType.MUST_START_ON;
            constraintDate = row.getDate("START_CONSTRAINT_DATE");
            break;
         }

         case 2:
         {
            constraintType = ConstraintType.START_NO_LATER_THAN;
            constraintDate = row.getDate("START_CONSTRAINT_DATE");
            break;
         }

         case 3:
         {
            constraintType = ConstraintType.START_NO_EARLIER_THAN;
            constraintDate = row.getDate("START_CONSTRAINT_DATE");
            break;
         }

         case 4:
         {
            constraintType = ConstraintType.MUST_FINISH_ON;
            constraintDate = row.getDate("END_CONSTRAINT_DATE");
            break;
         }

         case 5:
         {
            constraintType = ConstraintType.FINISH_NO_LATER_THAN;
            constraintDate = row.getDate("END_CONSTRAINT_DATE");
            break;
         }

         case 6:
         {
            constraintType = ConstraintType.FINISH_NO_EARLIER_THAN;
            constraintDate = row.getDate("END_CONSTRAINT_DATE");
            break;
         }

         case 8:
         {
            task.setDeadline(row.getDate("END_CONSTRAINT_DATE"));
            break;
         }
      }

      task.setConstraintType(constraintType);
      task.setConstraintDate(constraintDate);
   }

   /**
    * Creates a mapping between exception ID values and working/non-working days.
    *
    * @param rows rows from the exceptions table
    * @return exception map
    */
   public Map createExceptionTypeMap(List rows)
   {
      Map map = new HashMap();
      for (Row row : rows)
      {
         Integer id = row.getInteger("EXCEPTIONNID");
         DayType result;

         switch (row.getInt("UNIQUE_BIT_FIELD"))
         {
            case 8: // Working
            case 32: // Overtime
            case 128: //Weekend Working
            {
               result = DayType.WORKING;
               break;
            }

            case 4: // Non Working
            case 16: // Holiday
            case 64: // Weather
            case -2147483648: // Weekend
            default:
            {
               result = DayType.NON_WORKING;
               break;
            }
         }

         map.put(id, result);
      }
      return map;
   }

   /**
    * Creates a map of work pattern rows indexed by the primary key.
    *
    * @param rows work pattern rows
    * @return work pattern map
    */
   public Map createWorkPatternMap(List rows)
   {
      Map map = new HashMap();
      for (Row row : rows)
      {
         map.put(row.getInteger("WORK_PATTERNID"), row);
      }
      return map;
   }

   /**
    * Creates a map between a calendar ID and a list of
    * work pattern assignment rows.
    *
    * @param rows work pattern assignment rows
    * @return work pattern assignment map
    */
   public Map> createWorkPatternAssignmentMap(List rows)
   {
      Map> map = new HashMap>();
      for (Row row : rows)
      {
         Integer calendarID = row.getInteger("WORK_PATTERN_ASSIGNMENTID");
         List list = map.get(calendarID);
         if (list == null)
         {
            list = new LinkedList();
            map.put(calendarID, list);
         }
         list.add(row);
      }
      return map;
   }

   /**
    * Creates a map between a calendar ID and a list of exception assignment rows.
    *
    * @param rows exception assignment rows
    * @return exception assignment map
    */
   public Map> createExceptionAssignmentMap(List rows)
   {
      Map> map = new HashMap>();
      for (Row row : rows)
      {
         Integer calendarID = row.getInteger("EXCEPTION_ASSIGNMENTID");
         List list = map.get(calendarID);
         if (list == null)
         {
            list = new LinkedList();
            map.put(calendarID, list);
         }
         list.add(row);
      }
      return map;
   }

   /**
    * Creates a map between a work pattern ID and a list of time entry rows.
    *
    * @param rows time entry rows
    * @return time entry map
    */
   public Map> createTimeEntryMap(List rows)
   {
      Map> map = new HashMap>();
      for (Row row : rows)
      {
         Integer workPatternID = row.getInteger("TIME_ENTRYID");
         List list = map.get(workPatternID);
         if (list == null)
         {
            list = new LinkedList();
            map.put(workPatternID, list);
         }
         list.add(row);
      }
      return map;
   }

   /**
    * Creates a ProjectCalendar instance from the Asta data.
    *
    * @param calendarRow basic calendar data
    * @param workPatternMap work pattern map
    * @param workPatternAssignmentMap work pattern assignment map
    * @param exceptionAssignmentMap exception assignment map
    * @param timeEntryMap time entry map
    * @param exceptionTypeMap exception type map
    */
   public void processCalendar(Row calendarRow, Map workPatternMap, Map> workPatternAssignmentMap, Map> exceptionAssignmentMap, Map> timeEntryMap, Map exceptionTypeMap)
   {
      //
      // Create the calendar and add the default working hours
      //
      ProjectCalendar calendar = m_project.addCalendar();
      Integer dominantWorkPatternID = calendarRow.getInteger("DOMINANT_WORK_PATTERN");
      calendar.setUniqueID(calendarRow.getInteger("CALENDARID"));
      processWorkPattern(calendar, dominantWorkPatternID, workPatternMap, timeEntryMap, exceptionTypeMap);
      calendar.setName(calendarRow.getString("NAMK"));

      //
      // Add any additional working weeks
      //
      List rows = workPatternAssignmentMap.get(calendar.getUniqueID());
      if (rows != null)
      {
         for (Row row : rows)
         {
            Integer workPatternID = row.getInteger("WORK_PATTERN");
            if (!workPatternID.equals(dominantWorkPatternID))
            {
               ProjectCalendarWeek week = calendar.addWorkWeek();
               week.setDateRange(new DateRange(row.getDate("START_DATE"), row.getDate("END_DATE")));
               processWorkPattern(week, workPatternID, workPatternMap, timeEntryMap, exceptionTypeMap);
            }
         }
      }

      //
      // Add exceptions - not sure how exceptions which turn non-working days into working days are handled by Asta - if at all?
      //
      rows = exceptionAssignmentMap.get(calendar.getUniqueID());
      if (rows != null)
      {
         for (Row row : rows)
         {
            Date startDate = row.getDate("STARU_DATE");
            Date endDate = row.getDate("ENE_DATE");
            calendar.addCalendarException(startDate, endDate);
         }
      }

      m_eventManager.fireCalendarReadEvent(calendar);
   }

   /**
    * Populates a ProjectCalendarWeek instance from Asta work pattern data.
    *
    * @param week target ProjectCalendarWeek instance
    * @param workPatternID target work pattern ID
    * @param workPatternMap work pattern data
    * @param timeEntryMap time entry map
    * @param exceptionTypeMap exception type map
    */
   private void processWorkPattern(ProjectCalendarWeek week, Integer workPatternID, Map workPatternMap, Map> timeEntryMap, Map exceptionTypeMap)
   {
      week.setName(workPatternMap.get(workPatternID).getString("NAMN"));

      List timeEntryRows = timeEntryMap.get(workPatternID);
      if (timeEntryRows != null)
      {
         long lastEndTime = Long.MIN_VALUE;
         Day currentDay = Day.SUNDAY;
         ProjectCalendarHours hours = week.addCalendarHours(currentDay);
         Arrays.fill(week.getDays(), DayType.NON_WORKING);

         for (Row row : timeEntryRows)
         {
            Date startTime = row.getDate("START_TIME");
            Date endTime = row.getDate("END_TIME");
            if (startTime == null)
            {
               startTime = DateHelper.getDayStartDate(new Date(0));
            }

            if (endTime == null)
            {
               endTime = DateHelper.getDayEndDate(new Date(0));
            }

            if (startTime.getTime() > endTime.getTime())
            {
               Calendar cal = Calendar.getInstance();
               cal.setTime(endTime);
               cal.add(Calendar.DAY_OF_YEAR, 1);
               endTime = cal.getTime();
            }

            if (startTime.getTime() < lastEndTime)
            {
               currentDay = currentDay.getNextDay();
               hours = week.addCalendarHours(currentDay);
            }

            DayType type = exceptionTypeMap.get(row.getInteger("EXCEPTIOP"));
            if (type == DayType.WORKING)
            {
               hours.addRange(new DateRange(startTime, endTime));
               week.setWorkingDay(currentDay, DayType.WORKING);
            }

            lastEndTime = endTime.getTime();
         }
      }
   }

   /**
    * Extract note text.
    *
    * @param row task data
    * @return note text
    */
   private String getNotes(Row row)
   {
      String notes = row.getString("NOTET");
      if (notes != null)
      {
         if (notes.isEmpty())
         {
            notes = null;
         }
         else
         {
            if (notes.indexOf(LINE_BREAK) != -1)
            {
               notes = notes.replace(LINE_BREAK, "\n");
            }
         }
      }
      return notes;
   }

   private ProjectFile m_project;
   private EventManager m_eventManager;

   private static final Double COMPLETE = Double.valueOf(100);
   private static final Double INCOMPLETE = Double.valueOf(0);
   private static final Duration ZERO_HOURS = Duration.getInstance(0, TimeUnit.HOURS);
   private static final String LINE_BREAK = "|@|||";
   private static final RowComparator LEAF_COMPARATOR = new RowComparator("NATURAL_ORDER", "NATURAO_ORDER");
   private static final RowComparator BAR_COMPARATOR = new RowComparator("EXPANDED_TASK", "NATURAL_ORDER");

   private static final RelationType[] RELATION_TYPES =
   {
      RelationType.FINISH_START,
      RelationType.START_START,
      RelationType.FINISH_FINISH,
      RelationType.START_FINISH
   };
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy