/** * Copyright © 2002 Instituto Superior Técnico * * This file is part of FenixEdu Academic. * * FenixEdu Academic 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 3 of the License, or * (at your option) any later version. * * FenixEdu Academic 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 FenixEdu Academic. If not, see <http://www.gnu.org/licenses/>. */ package org.fenixedu.academic.servlet.taglib.sop.v3; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.PageContext; import org.apache.struts.Globals; import org.apache.struts.util.RequestUtils; import org.fenixedu.academic.domain.SchoolClass; import org.fenixedu.academic.dto.InfoShowOccupation; import org.fenixedu.academic.servlet.taglib.sop.v3.renderers.ClassTimeTableWithoutLinksLessonContentRenderer; import org.fenixedu.academic.servlet.taglib.sop.v3.renderers.ShiftEnrollmentTimeTableLessonContentRenderer; /** * @author jpvl */ public class TimeTableRenderer { // private Integer endHour; private final Integer startHour; private final Integer slotSize; private final TimeTable timeTable; private final LessonSlotContentRenderer lessonSlotContentRenderer; private final ColorPicker colorPicker; /** * Constructor TimeTableRenderer. * * @param timeTable * @param lessonSlotContentRenderer * @param slotSize * @param startHour * @param endHour * @param colorPicker */ public TimeTableRenderer(TimeTable timeTable, LessonSlotContentRenderer lessonSlotContentRenderer, Integer slotSize, Integer startHour, Integer endHour, ColorPicker colorPicker) { if (colorPicker == null) { throw new IllegalArgumentException(this.getClass().getName() + ":Color picker must be not null!"); } this.timeTable = timeTable; this.lessonSlotContentRenderer = lessonSlotContentRenderer; // this.endHour = endHour; this.startHour = startHour; this.slotSize = slotSize; this.colorPicker = colorPicker; } public StringBuilder render(Locale locale, PageContext pageContext, boolean definedWidth) { StringBuilder strBuffer = new StringBuilder(""); HashMap slotLessons = new HashMap(); TimeTableSlot[][] grid = timeTable.getTimeTableGrid(); strBuffer.append("<table class='timetable' cellspacing='0' cellpadding='0'"); if (definedWidth) { strBuffer.append("width='90%'"); } strBuffer.append(">"); final SchoolClass schoolClass = (SchoolClass) pageContext.getRequest().getAttribute("schoolClass"); if (schoolClass != null) { final String className = schoolClass.getNome(); strBuffer.append("<caption>"); final String captionPrefix = getMessageResource(pageContext, "public.degree.information.title.class.timetable", locale); strBuffer.append(captionPrefix); strBuffer.append(' '); strBuffer.append(className); strBuffer.append("</caption>"); } renderHeader(strBuffer, locale, pageContext, definedWidth); for (int hourIndex = 0; hourIndex < timeTable.getNumberOfHours().intValue(); hourIndex++) { strBuffer.append("<tr>\r\n"); strBuffer.append("<th class='period-hours'"); strBuffer.append(" id='hour"); strBuffer.append(hourIndex); strBuffer.append("'>"); strBuffer.append(getHourLabelByIndex(hourIndex)).append("</th>\r\n"); /* iterate over days */ for (int dayIndex = 0; dayIndex < timeTable.getNumberOfDays().intValue(); dayIndex++) { DayColumn dayColumn = timeTable.getDayColumn(dayIndex); TimeTableSlot timeTableSlot = grid[dayIndex][hourIndex]; if (timeTableSlot != null) { List colisionList = timeTableSlot.getLessonSlotList(); InfoLessonWrapper[] lessonSlotListResolved = resolveColisions(colisionList, timeTable.getDayColumn(dayIndex)); for (int slotIndex = 0; slotIndex < lessonSlotListResolved.length; slotIndex++) { InfoLessonWrapper infoLessonWrapper = lessonSlotListResolved[slotIndex]; strBuffer.append("<td "); if (infoLessonWrapper != null) { strBuffer.append("style='background-color: ") .append(colorPicker.getBackgroundColor(infoLessonWrapper)).append("' "); } int slotColspan = calculateColspan(infoLessonWrapper, lessonSlotListResolved, slotIndex, dayColumn); if (slotColspan > 1) { strBuffer.append(" colspan ='").append(slotColspan).append("'"); slotIndex += slotColspan - 1; } strBuffer.append(" headers='weekday"); strBuffer.append(dayIndex); for (int hourHeader = hourIndex; hourHeader < hourIndex + slotColspan; hourHeader++) { strBuffer.append(" hour"); strBuffer.append(hourHeader); } strBuffer.append("' "); strBuffer.append(" class='"); strBuffer.append(getSlotCssClass(infoLessonWrapper, hourIndex)); strBuffer.append("' "); if (infoLessonWrapper != null) { final InfoShowOccupation occupation = infoLessonWrapper.getLessonSlot().getInfoLessonWrapper().getInfoShowOccupation(); strBuffer.append(" title='") .append(this.lessonSlotContentRenderer.renderTitleText(infoLessonWrapper.getLessonSlot())) .append("'"); } strBuffer.append(">"); String contextPath = ((HttpServletRequest) pageContext.getRequest()).getContextPath(); // if infolessonwrapper start and end are the same... if ((infoLessonWrapper != null) && (infoLessonWrapper.getLessonSlot().getStartIndex() == hourIndex)) { final HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); final String context = request.getContextPath(); strBuffer.append(this.lessonSlotContentRenderer.render(context, infoLessonWrapper.getLessonSlot())); if (getSlotCssClass(infoLessonWrapper, hourIndex).equalsIgnoreCase("period-single-slot")) { strBuffer.append(this.lessonSlotContentRenderer.renderSecondLine(context, infoLessonWrapper.getLessonSlot())); } if (this.lessonSlotContentRenderer instanceof ShiftEnrollmentTimeTableLessonContentRenderer) { if (getSlotCssClass(infoLessonWrapper, hourIndex).equalsIgnoreCase("period-single-slot")) { LessonSlotContentRendererShift lessonSlotContentRendererShift = (LessonSlotContentRendererShift) this.lessonSlotContentRenderer; strBuffer.append(lessonSlotContentRendererShift.lastRender(infoLessonWrapper.getLessonSlot(), contextPath)); } slotLessons.put(slotIndex + "-" + dayIndex, infoLessonWrapper); } } else { if (infoLessonWrapper != null && !infoLessonWrapper.isSecondRowAlreadyAppended()) { final HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); final String context = request.getContextPath(); strBuffer.append(this.lessonSlotContentRenderer.renderSecondLine(context, infoLessonWrapper.getLessonSlot())); } if (this.lessonSlotContentRenderer instanceof ShiftEnrollmentTimeTableLessonContentRenderer && getSlotCssClass((InfoLessonWrapper) slotLessons.get(slotIndex + "-" + dayIndex), hourIndex) .equalsIgnoreCase("period-last-slot")) { LessonSlotContentRendererShift lessonSlotContentRendererShift = (LessonSlotContentRendererShift) this.lessonSlotContentRenderer; InfoLessonWrapper infoLessonWrapperHashMap = (InfoLessonWrapper) slotLessons.get(slotIndex + "-" + dayIndex); strBuffer.append(lessonSlotContentRendererShift.lastRender( infoLessonWrapperHashMap.getLessonSlot(), contextPath)); } else { if (infoLessonWrapper != null && this.lessonSlotContentRenderer instanceof ClassTimeTableWithoutLinksLessonContentRenderer) { final HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); final String context = request.getContextPath(); strBuffer.append(this.lessonSlotContentRenderer.render(context, infoLessonWrapper.getLessonSlot())); } strBuffer.append(" "); } } strBuffer.append("</td>\r\n"); } } else { /** no lessons */ int colspan = dayColumn.getMaxColisionSize().intValue(); String cssClass = getSlotCssClass(null, hourIndex); strBuffer.append("<td ").append(" class='").append(cssClass).append("' "); if (colspan > 1) { strBuffer.append("colspan='").append(colspan).append("'"); } strBuffer.append(">").append(" ").append("</td>\r\n"); } } strBuffer.append("</tr>\r\n"); } strBuffer.append("</table>"); return strBuffer; } private String getMessageResource(PageContext pageContext, String key, Locale locale) { try { return RequestUtils.message(pageContext, "PUBLIC_DEGREE_INFORMATION", Globals.LOCALE_KEY, key); } catch (JspException e) { return "???" + key + "???"; } } /** * @param infoLessonWrapper * @param lessonSlotListResolved * @param dayColumn * @param slotIndex * @return */ private int calculateColspan(InfoLessonWrapper infoLessonWrapper, InfoLessonWrapper[] lessonSlotListResolved, int currentSlotIndex, DayColumn dayColumn) { int colspan = 1; if (infoLessonWrapper != null && (infoLessonWrapper.getNumberOfCollisions().intValue() == 0)) { colspan = dayColumn.getMaxColisionSize().intValue(); } else if (infoLessonWrapper == null) { for (int i = currentSlotIndex + 1; i < lessonSlotListResolved.length; i++) { InfoLessonWrapper next = lessonSlotListResolved[i]; if (next == null) { colspan++; } else { break; } } } return colspan; } /** * * @param infoLessonWrapper * may be null * @param hourIndex * @param dayIndex * @param timeTableGrid * @param slotColisions * may be null * @param slotIndex * @return String */ private String getSlotCssClass(InfoLessonWrapper infoLessonWrapper, int hourIndex) { String css = "period-empty-slot"; if (infoLessonWrapper != null) { LessonSlot lessonSlot = infoLessonWrapper.getLessonSlot(); if ((lessonSlot.getStartIndex() == hourIndex) && (lessonSlot.getEndIndex() == hourIndex)) { css = "period-single-slot"; } else if (lessonSlot.getStartIndex() == hourIndex) { css = "period-first-slot"; } else if (lessonSlot.getEndIndex() == hourIndex) { css = "period-last-slot"; } else { css = "period-middle-slot"; } } return css; } /** * Method renderHeader. * * @param strBuffer * @param pageContext * @param definedWidth */ private void renderHeader(StringBuilder strBuffer, Locale locale, PageContext pageContext, boolean definedWidth) { // strBuffer.append("<th width='15%'>horas/dias</th>\r\n"); String hourDaysTitle; try { hourDaysTitle = RequestUtils.message(pageContext, "PUBLIC_DEGREE_INFORMATION", Globals.LOCALE_KEY, "public.degree.information.label.timesAndDays"); } catch (JspException e) { hourDaysTitle = "???label.timesAndDays???"; } strBuffer.append("<tr><th>"); strBuffer.append(hourDaysTitle); strBuffer.append("</th>\r\n"); int cellWidth = (100 - 15) / timeTable.getNumberOfDays().intValue(); for (int index = 0; index < this.timeTable.getNumberOfDays().intValue(); index++) { strBuffer.append("<th colspan='"); strBuffer.append(timeTable.getDayColumn(index).getMaxColisionSize()); strBuffer.append("' "); if (definedWidth) { strBuffer.append("width='"); strBuffer.append(cellWidth); strBuffer.append("%'"); } strBuffer.append(" id='weekday"); strBuffer.append(index); strBuffer.append("'"); strBuffer.append(">\r\n"); strBuffer.append(timeTable.getDayColumn(index).getLabel()); strBuffer.append("</th>\r\n"); } strBuffer.append("</tr>"); } protected InfoLessonWrapper[] resolveColisions(List lessonSlotList, DayColumn dayColumn) { InfoLessonWrapper[] list = new InfoLessonWrapper[dayColumn.getMaxColisionSize().intValue()]; Iterator lessonSlotListIterator = lessonSlotList.iterator(); List lessonSlotNotLocked = new ArrayList(); while (lessonSlotListIterator.hasNext()) { LessonSlot lessonSlot = (LessonSlot) lessonSlotListIterator.next(); InfoLessonWrapper infoLessonWrapper = lessonSlot.getInfoLessonWrapper(); if (infoLessonWrapper.isLocked()) { list[infoLessonWrapper.getSlotIndex()] = infoLessonWrapper; } else { lessonSlotNotLocked.add(infoLessonWrapper); } } Collections.sort(lessonSlotNotLocked); Iterator lessonSlotNotLockedIterator = lessonSlotNotLocked.iterator(); while (lessonSlotNotLockedIterator.hasNext()) { InfoLessonWrapper infoLessonWrapper = (InfoLessonWrapper) lessonSlotNotLockedIterator.next(); for (int index = 0; index < list.length; index++) { if (list[index] == null) { list[index] = infoLessonWrapper; infoLessonWrapper.setLocked(true, index); break; } } } return list; } private StringBuilder getHourLabelByIndex(int hourIndex) { StringBuilder label = new StringBuilder(""); double startLabelHour = startHour.doubleValue() + new Integer(hourIndex).doubleValue() / (60.0 / slotSize.doubleValue()); double startMinutes = startLabelHour - Math.floor(startLabelHour); String startMinutesLabel = String.valueOf((int) (startMinutes * 60)); if (startMinutesLabel.length() == 1) { startMinutesLabel = "0" + startMinutesLabel; } double endLabelHour = startHour.doubleValue() + new Integer(hourIndex + 1).doubleValue() / (60.0 / slotSize.doubleValue()); double endMinutes = endLabelHour - Math.floor(endLabelHour); String endMinutesLabel = String.valueOf((int) (endMinutes * 60)); if (endMinutesLabel.length() == 1) { endMinutesLabel = "0" + endMinutesLabel; } return label.append((int) startLabelHour).append(":").append(startMinutesLabel).append("-").append((int) endLabelHour) .append(":").append(endMinutesLabel); } }