org.unitime.timetable.action.CrossListsModifyAction.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.timetable.action.CrossListsModifyAction.java

Source

/*
 * UniTime 3.2 - 3.5 (University Timetabling Application)
 * Copyright (C) 2008 - 2013, UniTime LLC, and individual contributors
 * as indicated by the @authors tag.
 * 
 * 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/>.
 * 
*/
package org.unitime.timetable.action;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.action.ActionRedirect;
import org.apache.struts.util.MessageResources;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.unitime.commons.Debug;
import org.unitime.localization.impl.Localization;
import org.unitime.localization.messages.CourseMessages;
import org.unitime.timetable.defaults.ApplicationProperty;
import org.unitime.timetable.form.CrossListsModifyForm;
import org.unitime.timetable.interfaces.ExternalCourseCrosslistAction;
import org.unitime.timetable.interfaces.ExternalCourseOfferingRemoveAction;
import org.unitime.timetable.interfaces.ExternalInstructionalOfferingInCrosslistAddAction;
import org.unitime.timetable.model.ChangeLog;
import org.unitime.timetable.model.Class_;
import org.unitime.timetable.model.CourseOffering;
import org.unitime.timetable.model.CurriculumCourse;
import org.unitime.timetable.model.Department;
import org.unitime.timetable.model.Event;
import org.unitime.timetable.model.Exam;
import org.unitime.timetable.model.InstrOfferingConfig;
import org.unitime.timetable.model.InstructionalOffering;
import org.unitime.timetable.model.SchedulingSubpart;
import org.unitime.timetable.model.SubjectArea;
import org.unitime.timetable.model.comparators.CourseOfferingComparator;
import org.unitime.timetable.model.dao.CourseOfferingDAO;
import org.unitime.timetable.model.dao.InstructionalOfferingDAO;
import org.unitime.timetable.security.SessionContext;
import org.unitime.timetable.security.rights.Right;
import org.unitime.timetable.util.Constants;
import org.unitime.timetable.util.LookupTables;

/** 
 * MyEclipse Struts
 * Creation date: 07-15-2005
 * 
 * XDoclet definition:
 * @struts:action path="/courseOfferingEdit" name="instructionalOfferingListForm" input="/user/instructionalOfferingSearch.jsp" scope="request"
 *
 * @author Tomas Muller, Heston Fernandes, Stephanie Schluttenhofer, Zuzana Mullerova
 */
@Service("/crossListsModify")
public class CrossListsModifyAction extends Action {

    protected final static CourseMessages MSG = Localization.create(CourseMessages.class);

    @Autowired
    SessionContext sessionContext;

    // --------------------------------------------------------- Instance Variables

    // --------------------------------------------------------- Methods

    /** 
     * Method execute
     * @param mapping
     * @param form
     * @param request
     * @param response
     * @return ActionForward
     */
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        MessageResources rsc = getResources(request);
        CrossListsModifyForm frm = (CrossListsModifyForm) form;

        // Get operation
        String op = (request.getParameter("op") == null) ? (frm.getOp() == null || frm.getOp().length() == 0)
                ? (request.getAttribute("op") == null) ? null : request.getAttribute("op").toString()
                : frm.getOp() : request.getParameter("op");

        if (op == null)
            op = request.getParameter("hdnOp");

        if (op == null || op.trim().length() == 0)
            throw new Exception(MSG.errorOperationNotInterpreted() + op);

        // Course Offering Id
        String courseOfferingId = "";

        // Set up Lists
        frm.setOp(op);
        LookupTables.setupCourseOfferings(request, sessionContext, new LookupTables.CourseFilter() {
            @Override
            public boolean accept(CourseOffering course) {
                return course.getInstructionalOffering().isNotOffered();
            }
        });

        // First access to screen
        if (op.equalsIgnoreCase(MSG.actionCrossLists())) {

            courseOfferingId = (request.getParameter("uid") == null)
                    ? (request.getAttribute("uid") == null) ? null : request.getAttribute("uid").toString()
                    : request.getParameter("uid");

            doLoad(frm, courseOfferingId);
        }

        // Add a course offering
        if (op.equalsIgnoreCase(MSG.actionAddCourseToCrossList())) {
            // Validate data input
            ActionMessages errors = frm.validate(mapping, request);

            if (errors.size() == 0) {
                Long addedOffering = frm.getAddCourseOfferingId();
                CourseOfferingDAO cdao = new CourseOfferingDAO();
                CourseOffering co = cdao.get(addedOffering);

                // Check reservations limit
                frm.addToCourseOfferings(co,
                        sessionContext.getUser().getCurrentAuthority().hasRight(Right.DepartmentIndependent)
                                || sessionContext.getUser().getCurrentAuthority().hasQualifier(co.getDepartment()));
                frm.setAddCourseOfferingId(null);
            } else {
                saveErrors(request, errors);
            }
        }

        // Remove a course offering
        if (op.equalsIgnoreCase(rsc.getMessage("button.delete"))) {
            String deletedOffering = request.getParameter("deletedCourseOfferingId");
            if (deletedOffering != null && deletedOffering.trim().length() > 0)
                frm.removeFromCourseOfferings(deletedOffering);
        }

        // Update the course offering
        if (op.equalsIgnoreCase(MSG.actionUpdateCrossLists())) {
            // Validate data input
            ActionMessages errors = frm.validate(mapping, request);

            if (errors.size() == 0) {
                doUpdate(request, frm);
                ActionRedirect redirect = new ActionRedirect(mapping.findForward("instructionalOfferingDetail"));
                redirect.addParameter("io", frm.getInstrOfferingId());
                redirect.addParameter("op", "view");
                return redirect;
            } else {
                saveErrors(request, errors);
            }
        }

        // Determine if a course offering cannot be deleted
        setReadOnlyCourseId(request, frm);

        // Remove the courses that are already part of this offering from list of courses
        filterCourseOfferingList(request, frm);

        return mapping.findForward("crossListsModify");
    }

    /**
     * Ensures that all offerings that are part of the instructional offering
     * does not appear in the drop down list 
     * @param request
     * @param frm
     */
    private void filterCourseOfferingList(HttpServletRequest request, CrossListsModifyForm frm) {
        Collection existingOfferings = frm.getCourseOfferingIds();
        Collection offerings = (Collection) request.getAttribute(CourseOffering.CRS_OFFERING_LIST_ATTR_NAME);

        for (Iterator i = offerings.iterator(); i.hasNext();) {
            CourseOffering co = (CourseOffering) i.next();
            if ((!co.getInstructionalOffering().isNotOffered()
                    && !co.getInstructionalOffering().getInstrOfferingConfigs().isEmpty())
                    || co.getInstructionalOffering().getCourseOfferings().size() > 1) {
                i.remove();
                continue;
            }
            for (Iterator j = existingOfferings.iterator(); j.hasNext();) {
                String course = (String) j.next();
                if (course.equals(co.getUniqueId().toString()))
                    i.remove();
            }
        }

        request.setAttribute(CourseOffering.CRS_OFFERING_LIST_ATTR_NAME, offerings);
    }

    /**
     * Compares the modified offering to the original offering
     * If more than one offering is in common then all offerings show the 'Delete' icon
     * If only one offering is in common that offering cannot be deleted
     * @param request
     * @param frm
     */
    private void setReadOnlyCourseId(HttpServletRequest request, CrossListsModifyForm frm) {
        short ct = 0;
        String originalOfferings = frm.getOriginalOfferings();
        List courseOfferingIds = frm.getCourseOfferingIds();

        for (Iterator i = courseOfferingIds.iterator(); i.hasNext();) {
            String cid = i.next().toString();
            if (originalOfferings.indexOf(cid) > 0) {

                // More than one course from the original offering exists in the modified one
                if (++ct > 1) {
                    frm.setReadOnlyCrsOfferingId(null);
                    break;
                } else {
                    frm.setReadOnlyCrsOfferingId(new Long(cid));
                }
            }
        }

        Debug.debug("Read Only Ctr Course: " + frm.getReadOnlyCrsOfferingId());
    }

    /**
     * Update the instructional offering
     * @param request
     * @param frm
     */
    private void doUpdate(HttpServletRequest request, CrossListsModifyForm frm) throws Exception {

        // Get the modified offering
        List ids = frm.getCourseOfferingIds();
        String courseIds = Constants.arrayToStr(ids.toArray(), "", " ");
        String origCourseIds = frm.getOriginalOfferings();

        // Get Offering
        CourseOfferingDAO cdao = new CourseOfferingDAO();
        InstructionalOfferingDAO idao = new InstructionalOfferingDAO();
        InstructionalOffering io = idao.get(frm.getInstrOfferingId());
        Session hibSession = idao.getSession();
        hibSession.setFlushMode(FlushMode.MANUAL);
        Transaction tx = null;
        HashMap saList = new HashMap();
        List<CurriculumCourse> cc = new ArrayList<CurriculumCourse>();

        try {
            tx = hibSession.beginTransaction();
            StringTokenizer strTok = new StringTokenizer(origCourseIds);

            while (strTok.hasMoreTokens()) {

                String origCrs = strTok.nextToken();

                // 1. For all deleted courses - create new offering and make 'not offered'
                if (courseIds.indexOf(origCrs) < 0) {
                    Debug.debug("Course removed from offering: " + origCrs);

                    // Create new instructional offering 
                    InstructionalOffering io1 = new InstructionalOffering();
                    CourseOffering co1 = cdao.get(new Long(origCrs.trim()));

                    // Copy attributes of old instr offering - make not offered
                    io1.setDemand(io.getDemand());
                    io1.setLimit(io.getLimit());
                    io1.setNotOffered(new Boolean(true));
                    io1.setSession(io.getSession());
                    io1.setByReservationOnly(io.getByReservationOnly());

                    // Copy attributes of old crs offering - set controlling                   
                    CourseOffering co2 = (CourseOffering) co1.clone();
                    co2.setIsControl(new Boolean(true));

                    for (CurriculumCourse x : (List<CurriculumCourse>) hibSession
                            .createQuery("from CurriculumCourse where course.uniqueId = :courseId")
                            .setLong("courseId", co1.getUniqueId()).list()) {
                        cc.add(x.clone(co2));
                        x.getClassification().getCourses().remove(x);
                        hibSession.delete(x);
                    }

                    /*                   
                                       hibSession.saveOrUpdate(io1);
                                       hibSession.flush();
                    */
                    // Remove from original inst offr
                    Set offerings = io.getCourseOfferings();
                    for (Iterator i = offerings.iterator(); i.hasNext();) {

                        CourseOffering co3 = (CourseOffering) i.next();
                        if (co3.equals(co1)) {
                            // Remove from Subject Area
                            SubjectArea sa = co3.getSubjectArea();
                            sa.getCourseOfferings().remove(co1);
                            hibSession.saveOrUpdate(sa);
                            saList.put(sa.getSubjectAreaAbbreviation(), sa);
                        }
                    }

                    // Delete old course offering
                    io.removeCourseOffering(co1);

                    Event.deleteFromEvents(hibSession, co1);
                    Exam.deleteFromExams(hibSession, co1);

                    String className = ApplicationProperty.ExternalActionCourseOfferingRemove.value();
                    if (className != null && className.trim().length() > 0) {
                        ExternalCourseOfferingRemoveAction removeAction = (ExternalCourseOfferingRemoveAction) (Class
                                .forName(className).newInstance());
                        removeAction.performExternalCourseOfferingRemoveAction(co1, hibSession);
                    }
                    hibSession.delete(co1);

                    //io.setCourseOfferings(offerings);

                    hibSession.saveOrUpdate(io);
                    hibSession.flush();

                    // Add course to instructional offering
                    co2.setInstructionalOffering(io1);
                    io1.addTocourseOfferings(co2);

                    // Update
                    if (io1.getInstrOfferingPermId() == null)
                        io1.generateInstrOfferingPermId();
                    hibSession.saveOrUpdate(io1);
                    hibSession.flush();

                    hibSession.refresh(io);
                    hibSession.refresh(io1);
                    className = ApplicationProperty.ExternalActionInstructionalOfferingInCrosslistAdd.value();
                    if (className != null && className.trim().length() > 0) {
                        ExternalInstructionalOfferingInCrosslistAddAction addAction = (ExternalInstructionalOfferingInCrosslistAddAction) (Class
                                .forName(className).newInstance());
                        addAction.performExternalInstructionalOfferingInCrosslistAddAction(io1, hibSession);
                    }
                }

                // 2. For all existing courses - update controlling attribute and reservation limits
                else {
                    Debug.debug("Updating controlling course  and course reservation: " + origCrs);

                    // Update controlling course attribute
                    CourseOffering co = cdao.get(new Long(origCrs));
                    if (frm.getCtrlCrsOfferingId().equals(co.getUniqueId()))
                        co.setIsControl(new Boolean(true));
                    else
                        co.setIsControl(new Boolean(false));

                    // Update course reservation
                    int indx = frm.getIndex(origCrs);
                    try {
                        co.setReservation(ids.size() > 1 ? Integer.valueOf(frm.getLimits(indx)) : null);
                    } catch (NumberFormatException e) {
                        co.setReservation(null);
                    }

                    hibSession.saveOrUpdate(co);

                    hibSession.flush();
                    hibSession.refresh(co);
                }
            }

            // 3. For all added courses - delete all preferences and change the instr offering id  
            Vector addedOfferings = new Vector();
            StringTokenizer strTok2 = new StringTokenizer(courseIds);

            while (strTok2.hasMoreTokens()) {
                String course = strTok2.nextToken();

                // Course added to offering
                if (origCourseIds.indexOf(course) < 0) {

                    Debug.debug("Course added to offering: " + course);

                    CourseOffering co1 = cdao.get(new Long(course.trim()));
                    InstructionalOffering io1 = co1.getInstructionalOffering();
                    SubjectArea sa = io1.getControllingCourseOffering().getSubjectArea();
                    Set offerings = io1.getCourseOfferings();

                    // Copy course offerings
                    for (Iterator i = offerings.iterator(); i.hasNext();) {
                        CourseOffering co2 = (CourseOffering) i.next();
                        SubjectArea sa2 = co2.getSubjectArea();

                        // Create a copy
                        CourseOffering co3 = (CourseOffering) co2.clone();
                        if (frm.getCtrlCrsOfferingId().equals(co2.getUniqueId()))
                            co3.setIsControl(new Boolean(true));
                        else
                            co3.setIsControl(new Boolean(false));

                        for (CurriculumCourse x : (List<CurriculumCourse>) hibSession
                                .createQuery("from CurriculumCourse where course.uniqueId = :courseId")
                                .setLong("courseId", co2.getUniqueId()).list()) {
                            cc.add(x.clone(co3));
                            x.getClassification().getCourses().remove(x);
                            hibSession.delete(x);
                        }

                        addedOfferings.addElement(co3);

                        int indx = frm.getIndex(course);
                        try {
                            co3.setReservation(Integer.valueOf(frm.getLimits(indx)));
                        } catch (NumberFormatException e) {
                            co3.setReservation(null);
                        }

                        // Remove from collection
                        //i.remove();

                        sa2.getCourseOfferings().remove(co2);
                        hibSession.saveOrUpdate(sa2);
                        saList.put(sa2.getSubjectAreaAbbreviation(), sa2);

                        // Delete course offering
                        io1.removeCourseOffering(co2);
                        Event.deleteFromEvents(hibSession, co2);
                        Exam.deleteFromExams(hibSession, co2);
                        String className = ApplicationProperty.ExternalActionCourseOfferingRemove.value();
                        if (className != null && className.trim().length() > 0) {
                            ExternalCourseOfferingRemoveAction removeAction = (ExternalCourseOfferingRemoveAction) (Class
                                    .forName(className).newInstance());
                            removeAction.performExternalCourseOfferingRemoveAction(co2, hibSession);
                        }

                        hibSession.delete(co2);
                        hibSession.flush();

                        //hibSession.refresh(sa2);

                    }

                    //io1.setCourseOfferings(offerings);
                    //hibSession.saveOrUpdate(io1);
                    Event.deleteFromEvents(hibSession, io1);
                    Exam.deleteFromExams(hibSession, io1);

                    hibSession.delete(io1);
                    hibSession.flush();

                    hibSession.saveOrUpdate(sa);
                    saList.put(sa.getSubjectAreaAbbreviation(), sa);

                    //hibSession.refresh(sa);

                }
            }

            hibSession.flush();

            // Update Offering - Added Offerings       
            for (int i = 0; i < addedOfferings.size(); i++) {
                CourseOffering co3 = (CourseOffering) addedOfferings.elementAt(i);
                co3.setInstructionalOffering(io);
                io.addTocourseOfferings(co3);
                hibSession.saveOrUpdate(co3);

                hibSession.flush();
                hibSession.refresh(co3);

                hibSession.saveOrUpdate(io);
            }
            for (CurriculumCourse x : cc)
                hibSession.saveOrUpdate(x);

            // Update managing department on all classes
            Department dept = io.getControllingCourseOffering().getDepartment();
            Set cfgs = io.getInstrOfferingConfigs();
            for (Iterator iterCfg = cfgs.iterator(); iterCfg.hasNext();) {
                InstrOfferingConfig cfg = (InstrOfferingConfig) iterCfg.next();
                Set subparts = cfg.getSchedulingSubparts();
                for (Iterator iterSbp = subparts.iterator(); iterSbp.hasNext();) {
                    SchedulingSubpart subpart = (SchedulingSubpart) iterSbp.next();
                    Set classes = subpart.getClasses();
                    for (Iterator iterCls = classes.iterator(); iterCls.hasNext();) {
                        Class_ cls = (Class_) iterCls.next();
                        // Only change departmental class managing dept and not externally managed
                        if (!cls.getManagingDept().isExternalManager()) {
                            cls.setManagingDept(dept);
                            hibSession.saveOrUpdate(cls);
                        }
                    }
                }
            }

            ChangeLog.addChange(hibSession, sessionContext, io, ChangeLog.Source.CROSS_LIST,
                    ChangeLog.Operation.UPDATE, io.getControllingCourseOffering().getSubjectArea(), null);

            tx.commit();
            hibSession.flush();
            hibSession.clear();
            hibSession.refresh(io);

            // Refresh objects
            for (Iterator i1 = io.getInstrOfferingConfigs().iterator(); i1.hasNext();) {
                InstrOfferingConfig cfg = (InstrOfferingConfig) i1.next();
                for (Iterator i2 = cfg.getSchedulingSubparts().iterator(); i2.hasNext();) {
                    SchedulingSubpart ss = (SchedulingSubpart) i2.next();
                    for (Iterator i3 = ss.getClasses().iterator(); i3.hasNext();) {
                        Class_ c = (Class_) i3.next();
                        hibSession.refresh(c);
                    }
                    hibSession.refresh(ss);
                }
            }

            Set keys = saList.keySet();
            for (Iterator i1 = keys.iterator(); i1.hasNext();) {
                hibSession.refresh(saList.get(i1.next()));
            }
            String className = ApplicationProperty.ExternalActionCourseCrosslist.value();
            if (className != null && className.trim().length() > 0) {
                ExternalCourseCrosslistAction addAction = (ExternalCourseCrosslistAction) (Class.forName(className)
                        .newInstance());
                addAction.performExternalCourseCrosslistAction(io, hibSession);
            }

        } catch (Exception e) {
            Debug.error(e);
            try {
                if (tx != null && tx.isActive())
                    tx.rollback();
            } catch (Exception e1) {
            }
            throw e;
        }
    }

    /**
     * Loads the form with the offering detail
     * @param frm Form object
     * @param courseOfferingId Course Offering Id of controlling course
     * @param user User object
     */
    private void doLoad(CrossListsModifyForm frm, String courseOfferingId) throws Exception {

        // Check uniqueid
        if (courseOfferingId == null || courseOfferingId.trim().length() == 0)
            throw new Exception(MSG.errorUniqueIdNeeded());

        // Load details
        CourseOfferingDAO coDao = new CourseOfferingDAO();
        CourseOffering co = coDao.get(Long.valueOf(courseOfferingId));
        InstructionalOffering io = co.getInstructionalOffering();

        sessionContext.checkPermission(io, Right.InstructionalOfferingCrossLists);

        // Sort Offerings
        ArrayList offerings = new ArrayList(io.getCourseOfferings());
        Collections.sort(offerings, new CourseOfferingComparator(CourseOfferingComparator.COMPARE_BY_CTRL_CRS));

        // Load form properties
        frm.setInstrOfferingId(io.getUniqueId());
        frm.setCtrlCrsOfferingId(io.getControllingCourseOffering().getUniqueId());
        frm.setReadOnlyCrsOfferingId(null);
        frm.setSubjectAreaId(co.getSubjectArea().getUniqueId());
        frm.setInstrOfferingName(io.getCourseNameWithTitle());
        frm.setOwnedInstrOffr(true); //?? new Boolean(io.isEditableBy(user)));
        frm.setIoLimit(io.getLimit());
        frm.setUnlimited(io.hasUnlimitedEnrollment());

        for (Iterator i = offerings.iterator(); i.hasNext();) {
            CourseOffering co1 = ((CourseOffering) i.next());
            frm.addToCourseOfferings(co1,
                    sessionContext.getUser().getCurrentAuthority().hasRight(Right.DepartmentIndependent)
                            || sessionContext.getUser().getCurrentAuthority().hasQualifier(co1.getDepartment()));
            frm.addToOriginalCourseOfferings(co1);
        }
    }
}