net.rrm.ehour.ui.timesheet.panel.TimesheetRowList.java Source code

Java tutorial

Introduction

Here is the source code for net.rrm.ehour.ui.timesheet.panel.TimesheetRowList.java

Source

/*
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package net.rrm.ehour.ui.timesheet.panel;

import com.google.common.collect.Lists;
import net.rrm.ehour.data.DateRange;
import net.rrm.ehour.domain.ProjectAssignment;
import net.rrm.ehour.ui.EhourWebApplication;
import net.rrm.ehour.ui.common.form.FormHighlighter;
import net.rrm.ehour.ui.timesheet.dto.GrandTotal;
import net.rrm.ehour.ui.timesheet.dto.ProjectTotalModel;
import net.rrm.ehour.ui.timesheet.dto.TimesheetCell;
import net.rrm.ehour.ui.timesheet.dto.TimesheetRow;
import net.rrm.ehour.ui.timesheet.model.TimesheetContainer;
import net.rrm.ehour.ui.timesheet.panel.renderer.TimesheetIconRenderFactory;
import net.rrm.ehour.ui.timesheet.panel.renderer.TimesheetIconRenderFactoryCollection;
import net.rrm.ehour.util.DateUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.RepeatingView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.spring.injection.annot.SpringBean;

import java.util.Calendar;
import java.util.List;

/**
 * Representation of a timesheet row
 */

public class TimesheetRowList extends ListView<TimesheetRow> {
    private static final long serialVersionUID = -6905022018110510887L;
    private static final String DAY_ID = "day";
    private static final String DAY_OPTIONS_ID = "options";

    private final GrandTotal grandTotals;
    private Form<?> form;

    private final IModel<TimesheetContainer> timesheetContainerModel;
    private MarkupContainer provider;

    @SpringBean
    private TimesheetIconRenderFactoryCollection iconRenderer;

    public TimesheetRowList(String id, List<TimesheetRow> model, GrandTotal grandTotals,
            IModel<TimesheetContainer> timesheetContainerModel, Form<?> form, MarkupContainer provider) {
        super(id, model);
        this.timesheetContainerModel = timesheetContainerModel;
        this.provider = provider;
        setReuseItems(true);
        this.grandTotals = grandTotals;
        this.form = form;
    }

    @Override
    protected void populateItem(ListItem<TimesheetRow> item) {
        final TimesheetRow row = item.getModelObject();
        ProjectAssignment assignment = row.getProjectAssignment();

        item.add(createBookWholeWeekLink(row, "bookWholeWeek"));
        item.add(new Label("project", assignment.getProject().getName()));
        Label role = new Label("role", String.format("(%s)", assignment.getRole()));
        role.setVisible(StringUtils.isNotBlank(assignment.getRole()));
        item.add(role);
        item.add(new Label("projectCode", assignment.getProject().getProjectCode()));
        item.add(createStatusLabel(item));
        addInputCells(item, row);
        item.add(createTotalHoursLabel(row));
    }

    private Component createBookWholeWeekLink(final TimesheetRow row, final String bookWholeWeek) {
        final AjaxLink<Void> link = new AjaxLink<Void>(bookWholeWeek) {
            private static final long serialVersionUID = -663239917205218384L;

            @Override
            public void onClick(AjaxRequestTarget target) {
                row.bookRemainingHoursOnRow();
                target.add(form);
            }
        };
        link.setVisible(EhourWebApplication.get().isBookWholeWeekEnabled() && !row.getTimesheet().isAnyLocked());
        return link;
    }

    private Label createStatusLabel(ListItem<TimesheetRow> item) {
        Label label = new Label("status", new PropertyModel<String>(item.getModel(), "status")) {
            @Override
            public boolean isVisible() {
                return StringUtils.isNotBlank(getDefaultModelObjectAsString());
            }
        };

        label.setEscapeModelStrings(false);
        label.setOutputMarkupId(true);
        label.setOutputMarkupPlaceholderTag(true);
        return label;
    }

    private Label createTotalHoursLabel(final TimesheetRow row) {
        final Label totalHours = new Label("total", new ProjectTotalModel(row)) {
            @Override
            public void onEvent(IEvent<?> event) {
                if (event.getPayload() instanceof TimesheetInputModifiedEvent) {
                    TimesheetInputModifiedEvent payload = (TimesheetInputModifiedEvent) event.getPayload();

                    if (payload.getForAssignment().equals(row.getProjectAssignment())) {
                        payload.getTarget().add(this);
                    }
                }
            }
        };

        totalHours.setOutputMarkupId(true);
        return totalHours;
    }

    private void addInputCells(ListItem<TimesheetRow> item, final TimesheetRow row) {
        Calendar currentDate = (Calendar) row.getFirstDayOfWeekDate().clone();

        ProjectAssignment assignment = row.getProjectAssignment();
        final DateRange range = new DateRange(assignment.getDateStart(), assignment.getDateEnd());

        final List<Calendar> dates = Lists.newArrayList();

        for (int i = 1; i <= 7; i++, currentDate.add(Calendar.DAY_OF_YEAR, 1)) {
            dates.add((Calendar) currentDate.clone());
        }

        item.add(new ListView<Calendar>("days", dates) {
            @Override
            protected void populateItem(ListItem<Calendar> item) {
                Calendar currentDate = item.getModelObject();

                int index = currentDate.get(Calendar.DAY_OF_WEEK) - 1;

                TimesheetCell timesheetCell = row.getTimesheetCells()[index];

                DayStatus dayStatus = determineDayStatus(timesheetCell, currentDate, range);

                Component day;

                switch (dayStatus) {
                case HIDDEN:
                default:
                    day = createEmptyTimesheetEntry(DAY_ID);
                    item.add(new WebMarkupContainer(DAY_OPTIONS_ID));
                    break;
                case OPEN:
                    day = createInputTimesheetEntry(DAY_ID, row, index);
                    item.add(renderOptions(DAY_OPTIONS_ID, timesheetCell, dayStatus));
                    break;
                case LOCKED:
                    day = createLockedTimesheetEntry(DAY_ID, row, index);
                    item.add(renderOptions(DAY_OPTIONS_ID, timesheetCell, dayStatus));
                    item.add(AttributeModifier.append("class", "locked"));
                    break;
                }

                String cssClass = "weekday";

                if (dayStatus == DayStatus.LOCKED) {
                    cssClass += " lockedCell";
                } else if (item.getIndex() == 0) {
                    cssClass = "sunday";
                } else if (item.getIndex() == 6) {
                    cssClass = "saturday";
                }

                day.add(AttributeModifier.replace("class", cssClass));
                item.add(day);
            }
        });
    }

    private DayStatus determineDayStatus(TimesheetCell timesheetCell, Calendar currentDate, DateRange range) {
        if (DateUtil.isDateWithinRange(currentDate, range)) {
            return timesheetCell.isLocked() ? DayStatus.LOCKED : DayStatus.OPEN;
        } else {
            return timesheetCell.isWithHours() ? DayStatus.LOCKED : DayStatus.HIDDEN;
        }
    }

    private Fragment createLockedTimesheetEntry(String id, TimesheetRow row, int index) {
        Fragment fragment = new Fragment(id, "dayLocked", provider);

        TimesheetCell timesheetCell = row.getTimesheetCells()[index];
        PropertyModel<Float> cellModel = new PropertyModel<>(timesheetCell, "timesheetEntry.hours");

        String css = timesheetCell.getTimesheetEntry() != null
                && StringUtils.isNotBlank(timesheetCell.getTimesheetEntry().getComment()) ? "lockedday"
                        : "lockeddaynocomment";
        fragment.add(AttributeModifier.append("class", css));

        // make sure it's added to the grandtotal
        grandTotals.addValue(index, timesheetCell, cellModel);

        fragment.add(new Label(DAY_ID, cellModel));

        return fragment;
    }

    private RepeatingView renderOptions(String id, TimesheetCell timesheetCell, DayStatus status) {
        RepeatingView options = new RepeatingView(id);

        for (TimesheetIconRenderFactory renderFactory : iconRenderer.getRenderFactories()) {
            options.add(renderFactory.renderForId(options.newChildId(), timesheetCell, status,
                    timesheetContainerModel));
        }

        return options;
    }

    private Fragment createEmptyTimesheetEntry(String id) {
        return new Fragment(id, "dayInputHidden", provider);
    }

    private Fragment createInputTimesheetEntry(String id, TimesheetRow row, final int index) {
        TimesheetCell timesheetCell = row.getTimesheetCells()[index];

        Fragment fragment = new Fragment(id, "dayInput", provider);
        fragment.add(createTextFieldWithValidation(timesheetCell, index));

        return fragment;
    }

    private TimesheetTextField createTextFieldWithValidation(final TimesheetCell timesheetCell, final int index) {
        PropertyModel<Float> cellModel = new PropertyModel<>(timesheetCell, "timesheetEntry.hours");

        // make sure it's added to the grandtotal
        grandTotals.addValue(index, timesheetCell, cellModel);

        // add inputfield with validation to the parent
        final TimesheetTextField dayInput = new TimesheetTextField(DAY_ID, cellModel, 1);
        dayInput.setOutputMarkupId(true);

        // add validation
        AjaxFormComponentUpdatingBehavior behavior = new AjaxFormComponentUpdatingBehavior("onblur") {
            private static final long serialVersionUID = 1L;

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                Form sink = dayInput.findParent(Form.class);
                send(sink, Broadcast.DEPTH,
                        new TimesheetInputModifiedEvent(target, index, timesheetCell.getProjectAssignment()));
                form.visitFormComponents(new FormHighlighter(target));
            }

            @Override
            protected void onError(final AjaxRequestTarget target, RuntimeException e) {
                form.visitFormComponents(new FormHighlighter(target));
            }
        };

        dayInput.add(behavior);

        return dayInput;
    }

    public enum DayStatus {
        OPEN, LOCKED, HIDDEN
    }
}