/**
* ***************************************************************************
* Copyright (c) 2010 Qcadoo Limited
* Project: Qcadoo Framework
* Version: 1.4
*
* This file is part of Qcadoo.
*
* Qcadoo is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* ***************************************************************************
*/
package com.qcadoo.view.internal.components.ganttChart;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.qcadoo.model.internal.api.ValueAndError;
import com.qcadoo.model.internal.types.DateType;
import com.qcadoo.view.api.components.ganttChart.GanttChartItem;
import com.qcadoo.view.api.components.ganttChart.GanttChartItemResolver;
import com.qcadoo.view.api.components.ganttChart.GanttChartItemStrip.Orientation;
import com.qcadoo.view.internal.components.ganttChart.GanttChartScaleImpl.ZoomLevel;
import com.qcadoo.view.internal.states.AbstractComponentState;
public class GanttChartComponentState extends AbstractComponentState {
public final GanttChartComponentEventPerformer eventPerformer = new GanttChartComponentEventPerformer();
private GanttChartScaleImpl scale;
private Orientation stripsOrientation;
private String dateFromErrorMessage;
private String dateToErrorMessage;
private String globalErrorMessage;
Map<String, List<GanttChartItem>> items;
Map<String, List<GanttChartItem>> collisionItems;
protected static final DateType DATETYPE = new DateType();
private final GanttChartItemResolver itemResolver;
private final int defaultStartDay;
private final int defaultEndDay;
private final ZoomLevel defaultZoomLevel;
private Long selectedEntityId;
private JSONObject context;
private final int itemsBorderWidth;
private final String itemsBorderColor;
public GanttChartComponentState(final GanttChartItemResolver itemResolver, final GanttChartComponentPattern pattern) {
super(pattern);
this.itemResolver = itemResolver;
this.defaultZoomLevel = pattern.getDefaultZoomLevel();
this.defaultStartDay = pattern.getDefaultStartDay();
this.defaultEndDay = pattern.getDefaultEndDay();
this.stripsOrientation = pattern.getStripOrientation();
this.itemsBorderWidth = pattern.getItemsBorderWidth();
this.itemsBorderColor = pattern.getItemsBorderColor();
registerEvent("refresh", eventPerformer, "refresh");
registerEvent("initialize", eventPerformer, "initialize");
registerEvent("select", eventPerformer, "selectEntity");
}
@Override
protected void initializeContext(final JSONObject json) throws JSONException {
super.initializeContext(json);
this.context = json;
}
@Override
public Object getFieldValue() {
return selectedEntityId;
}
@Override
protected void initializeContent(final JSONObject json) throws JSONException {
JSONObject headerDataObject = json.getJSONObject("headerParameters");
ZoomLevel zoomLevel = ZoomLevel.valueOf(headerDataObject.getString("scale"));
String dateFromString = headerDataObject.getString("dateFrom");
String dateToString = headerDataObject.getString("dateTo");
DateTime now = new DateTime().withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0);
Date dateFrom = now.plusDays(defaultStartDay).toDate();
Date dateTo = now.plusDays(defaultEndDay).toDate();
if (dateFromString == null || "".equals(dateFromString)) {
dateFromErrorMessage = translate("errorMessage.emptyDate");
} else {
ValueAndError dateFromVaE = DATETYPE.toObject(null, dateFromString);
if (dateFromVaE.getMessage() == null) {
dateFrom = (Date) dateFromVaE.getValue();
} else {
dateFromErrorMessage = translate("errorMessage.dateNotValid");
}
}
if (dateToString == null || "".equals(dateToString)) {
dateToErrorMessage = translate("errorMessage.emptyDate");
} else {
ValueAndError dateToVaE = DATETYPE.toObject(null, dateToString);
if (dateToVaE.getMessage() == null) {
dateTo = (Date) dateToVaE.getValue();
} else {
dateToErrorMessage = translate("errorMessage.dateNotValid");
}
}
scale = new GanttChartScaleImpl(this, zoomLevel, dateFrom, dateTo);
if (dateFromErrorMessage == null && globalErrorMessage == null) {
if (scale.isFromLargerThanTo()) {
globalErrorMessage = translate("errorMessage.fromLargerThanTo");
} else if (scale.isTooLargeRange()) {
globalErrorMessage = translate("errorMessage.tooLargeRange", String.valueOf(scale.getMaxRangeInMonths()));
}
}
if (json.has("selectedEntityId")) {
selectedEntityId = json.getLong("selectedEntityId");
}
}
@Override
protected JSONObject renderContent() throws JSONException {
JSONObject json = new JSONObject();
json.put("zoomLevel", scale.getZoomLevel().toString());
json.put("dateFromErrorMessage", dateFromErrorMessage);
json.put("dateToErrorMessage", dateToErrorMessage);
if (dateFromErrorMessage == null) {
json.put("dateFrom", DATETYPE.toString(scale.getDateFrom(), getLocale()));
}
if (dateToErrorMessage == null) {
json.put("dateTo", DATETYPE.toString(scale.getDateTo(), getLocale()));
}
json.put("globalErrorMessage", globalErrorMessage);
if (globalErrorMessage == null) {
json.put("scale", scale.getAsJson());
JSONArray rowsArray = new JSONArray();
JSONArray itemsArray = new JSONArray();
for (Map.Entry<String, List<GanttChartItem>> entry : items.entrySet()) {
rowsArray.put(entry.getKey());
for (GanttChartItem item : entry.getValue()) {
if (item != null) {
itemsArray.put(item.getAsJson());
}
}
}
json.put("stripsOrientation", getStripsOrientation().getStringValue());
json.put("itemsBorderColor", itemsBorderColor);
json.put("itemsBorderWidth", itemsBorderWidth);
json.put("rows", rowsArray);
json.put("items", itemsArray);
JSONArray collisionItemsArray = new JSONArray();
for (Map.Entry<String, List<GanttChartItem>> entry : collisionItems.entrySet()) {
for (GanttChartItem item : entry.getValue()) {
if (item != null) {
collisionItemsArray.put(item.getAsJson());
}
}
}
json.put("collisions", collisionItemsArray);
json.put("selectedEntityId", selectedEntityId);
}
return json;
}
private Orientation getStripsOrientation() {
if (stripsOrientation == null) {
return Orientation.HORIZONTAL;
} else {
return stripsOrientation;
}
}
protected String translate(final String suffix, final String... args) {
return getTranslationService().translate(getTranslationPath() + "." + suffix, "qcadooView.gantt." + suffix, getLocale(),
args);
}
protected class GanttChartComponentEventPerformer {
public void initialize(final String[] args) {
DateTime now = new DateTime().withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0).withMillisOfSecond(0);
scale = new GanttChartScaleImpl(GanttChartComponentState.this, defaultZoomLevel, now.plusDays(defaultStartDay)
.toDate(), now.plusDays(defaultEndDay).toDate());
scale.setIsDatesSet(true);
dateFromErrorMessage = null;
dateToErrorMessage = null;
globalErrorMessage = null;
refresh(args);
}
public void refresh(final String[] args) {
requestRender();
requestUpdateState();
if (globalErrorMessage != null) {
return;
}
items = itemResolver.resolve(scale, context, getLocale());
updateCollisionItems();
}
public void selectEntity(final String[] args) {
notifyEntityIdChangeListeners(selectedEntityId);
}
private void updateCollisionItems() {
collisionItems = new HashMap<String, List<GanttChartItem>>();
for (Entry<String, List<GanttChartItem>> rowEntry : items.entrySet()) {
List<GanttChartItem> sortedItems = new ArrayList<GanttChartItem>(rowEntry.getValue());
Collections.sort(sortedItems, new Comparator<GanttChartItem>() {
@Override
public int compare(final GanttChartItem itemA, final GanttChartItem itemB) {
return Double.valueOf(itemA.getFrom()).compareTo(Double.valueOf(itemB.getFrom()));
}
});
List<GanttChartItem> collisionRow = getCollisionsList(sortedItems, rowEntry.getKey());
if (!collisionRow.isEmpty()) {
collisionItems.put(rowEntry.getKey(), collisionRow);
}
}
}
private List<GanttChartItem> getCollisionsList(final List<GanttChartItem> sortedItems, final String row) {
List<GanttChartItem> collisionRow = new ArrayList<GanttChartItem>();
GanttChartConflictItem collisionItem = null;
GanttChartItem previousItem = null;
for (GanttChartItem item : sortedItems) {
if (previousItem != null && item.getFrom() < previousItem.getTo()) {
if (collisionItem != null && item.getFrom() < collisionItem.getTo()) { // same collision
if (item.getTo() > collisionItem.getTo()) { // not entirely included in existing collision
if (item.getTo() > previousItem.getTo()) { // entirely included in previous item
collisionItem.setTo(item.getTo());
collisionItem.setDateTo(item.getDateTo());
} else {
collisionItem.setTo(previousItem.getTo());
collisionItem.setDateTo(previousItem.getDateTo());
}
}
} else { // different collision
if (collisionItem != null) {
collisionRow.add(collisionItem);
}
if (item.getTo() < previousItem.getTo()) { // entirely included in previous item
collisionItem = new GanttChartConflictItem(row, item.getDateFrom(), item.getDateTo(), item.getFrom(),
item.getTo());
} else {
collisionItem = new GanttChartConflictItem(row, item.getDateFrom(), previousItem.getDateTo(),
item.getFrom(), previousItem.getTo());
}
collisionItem.addItem(previousItem);
}
collisionItem.addItem(item);
}
if (previousItem == null || item.getTo() > previousItem.getTo()) {
previousItem = item;
}
}
if (collisionItem != null) {
collisionRow.add(collisionItem);
}
return collisionRow;
}
}
}