Java tutorial
/* * Version: 1.0 * * The contents of this file are subject to the OpenVPMS License Version * 1.0 (the 'License'); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.openvpms.org/license/ * * Software distributed under the License is distributed on an 'AS IS' basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Copyright 2018 (C) OpenVPMS Ltd. All Rights Reserved. */ package org.openvpms.web.workspace.workflow.appointment; import org.apache.commons.lang.ObjectUtils; import org.joda.time.DateTime; import org.joda.time.Days; import org.openvpms.archetype.rules.util.DateRules; import org.openvpms.archetype.rules.util.DateUnits; import org.openvpms.archetype.rules.workflow.AppointmentRules; import org.openvpms.archetype.rules.workflow.ScheduleEvent; import org.openvpms.archetype.rules.workflow.ScheduleEvents; import org.openvpms.component.business.domain.im.common.IMObjectReference; import org.openvpms.component.business.service.archetype.helper.IMObjectBean; import org.openvpms.component.model.entity.Entity; import org.openvpms.component.system.common.cache.IMObjectCache; import org.openvpms.component.system.common.cache.SoftRefIMObjectCache; import org.openvpms.component.system.common.util.PropertySet; import org.openvpms.web.system.ServiceHelper; import org.openvpms.web.workspace.workflow.scheduling.Schedule; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; /** * Multiple-day schedule grid. * * @author Tim Anderson */ public abstract class AbstractMultiDayScheduleGrid extends AbstractScheduleEventGrid { /** * The number of days to display. */ private final int days; /** * Constructs an {@link AbstractMultiDayScheduleGrid}. * * @param scheduleView the schedule view * @param date the date * @param days the number of days to display * @param events the events * @param rules the appointment rules */ public AbstractMultiDayScheduleGrid(Entity scheduleView, Date date, int days, Map<Entity, ScheduleEvents> events, AppointmentRules rules) { super(scheduleView, date, DateRules.getDate(date, days - 1, DateUnits.DAYS), rules); this.days = days; setEvents(events); } /** * Returns the no. of slots in the grid. * * @return the no. of slots */ @Override public int getSlots() { return days; } /** * Returns the event for the specified schedule and slot. * * @param schedule the schedule * @param slot the slot * @return the corresponding event, or {@code null} if none is found */ @Override public PropertySet getEvent(Schedule schedule, int slot) { return getEvent(schedule, slot, true); } /** * Returns the event for the specified schedule and slot. * * @param schedule the schedule * @param slot the slot * @param includeBlockingEvents if {@code true}, look for blocking events if there are no non-blocking events * @return the corresponding event, or {@code null} if none is found */ public PropertySet getEvent(Schedule schedule, int slot, boolean includeBlockingEvents) { Date time = getStartTime(schedule, slot); PropertySet result = schedule.getEvent(time, 24 * 60, includeBlockingEvents); if (result == null) { result = schedule.getIntersectingEvent(time, includeBlockingEvents); } return result; } /** * Returns the time that the specified slot starts at. * * @param schedule the schedule * @param slot the slot * @return the start time of the specified slot. May be {@code null} */ @Override public Date getStartTime(Schedule schedule, int slot) { Date date = DateRules.getDate(getStartDate(), slot, DateUnits.DAYS); return DateRules.getDate(date, schedule.getStartMins(), DateUnits.MINUTES); } /** * Determines the availability of a slot for the specified schedule. * * @param schedule the schedule * @param slot the slot * @return the availability of the schedule */ @Override public Availability getAvailability(Schedule schedule, int slot) { PropertySet event = getEvent(schedule, slot, false); return (event != null) ? Availability.BUSY : Availability.FREE; } /** * Determines how many slots are unavailable from the specified slot, for * a schedule. * * @param schedule the schedule * @param slot the starting slot * @return the no. of concurrent slots that are unavailable */ @Override public int getUnavailableSlots(Schedule schedule, int slot) { return 0; } /** * Returns the slot that a time falls in. * * @param time the time * @return the slot, or {@code -1} if the time doesn't intersect any slot */ @Override public int getSlot(Date time) { return Days.daysBetween(new DateTime(getStartDate()), new DateTime(time)).getDays(); } /** * Returns the no. of slots an event occupies, from the specified slot. * <p> * If the event begins prior to the slot, the remaining slots will be returned. * * @param event the event * @param slot the starting slot * @return the no. of slots that the event occupies */ public int getSlots(PropertySet event, int slot) { DateTime endTime = new DateTime(event.getDate(ScheduleEvent.ACT_END_TIME)); int endSlot = Days.daysBetween(new DateTime(getStartDate()), endTime).getDays(); if (endTime.getHourOfDay() > 0 || endTime.getMinuteOfHour() > 0) { ++endSlot; } return endSlot - slot; } /** * Returns the date of a slot. * * @param slot the slot * @return the start time of the specified slot */ public Date getDate(int slot) { return DateRules.getDate(getStartDate(), slot, DateUnits.DAYS); } /** * Sets the events. * * @param events the events, keyed on schedule */ protected void setEvents(Map<Entity, ScheduleEvents> events) { List<Schedule> schedules = new ArrayList<>(); IMObjectCache cageTypes = new SoftRefIMObjectCache(ServiceHelper.getArchetypeService()); int index = -1; Entity last = null; for (Entity entity : events.keySet()) { IMObjectBean bean = new IMObjectBean(entity); Entity cageType = (Entity) cageTypes.get(bean.getNodeTargetObjectRef("cageType")); Schedule schedule = new Schedule(entity, cageType, 0, 24 * 60, 24 * 60, getAppointmentRules()); if (!ObjectUtils.equals(last, entity)) { index++; } last = entity; schedule.setRenderEven(index % 2 == 0); schedules.add(schedule); } setSchedules(schedules); // add the events for (Map.Entry<Entity, ScheduleEvents> entry : events.entrySet()) { Entity schedule = entry.getKey(); List<PropertySet> sets = entry.getValue().getEvents(); for (PropertySet set : sets) { addEvent(schedule, set); } } } /** * Adds an event or blocking event. * <p> * If the event is not a blocking event, and the corresponding Schedule already has an event that intersects * it, a new Schedule will be created with the same start and end times, and the event added to that. * * @param schedule the schedule to add the appointment to * @param event the event */ @Override protected void addEvent(Entity schedule, PropertySet event) { int index = -1; boolean found = false; Schedule column = null; Schedule match = null; boolean blockingEvent = Schedule.isBlockingEvent(event); // try and find a corresponding Schedule. If the event is non-blocking, try and find one that has no event // that intersects the supplied one. List<Schedule> columns = getSchedules(); IMObjectReference eventRef = event.getReference(ScheduleEvent.ACT_REFERENCE); for (int i = 0; i < columns.size(); ++i) { column = columns.get(i); if (column.getSchedule().equals(schedule)) { if (column.indexOf(eventRef) != -1) { // multi-day appointments are duplicated on subsequent days, so skip it if it has already been added return; } if (blockingEvent) { found = true; break; } else if (column.hasIntersectingEvent(event)) { match = column; index = i; } else { found = true; break; } } } if (!found) { // event intersects an existing one, so create a new Schedule. Any blocking event will be shared. column = new Schedule(match, getAppointmentRules()); columns.add(index + 1, column); } column.addEvent(event); } }