package org.sigmah.offline.handler; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * 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/gpl-3.0.html>. * #L% */ import com.extjs.gxt.ui.client.data.RpcMap; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import org.sigmah.client.computation.ClientValueResolver; import org.sigmah.client.dispatch.DispatchListener; import org.sigmah.offline.dao.ComputationAsyncDAO; import org.sigmah.offline.dao.MonitoredPointAsyncDAO; import org.sigmah.offline.dao.PersonalCalendarAsyncDAO; import org.sigmah.offline.dao.ProjectAsyncDAO; import org.sigmah.offline.dao.ProjectReportAsyncDAO; import org.sigmah.offline.dao.ReminderAsyncDAO; import org.sigmah.offline.dao.RequestManager; import org.sigmah.offline.dao.RequestManagerCallback; import org.sigmah.offline.dao.UpdateDiaryAsyncDAO; import org.sigmah.offline.dao.ValueAsyncDAO; import org.sigmah.offline.dispatch.AsyncCommandHandler; import org.sigmah.offline.dispatch.OfflineExecutionContext; import org.sigmah.offline.dispatch.UnavailableCommandException; import org.sigmah.offline.sync.AsyncAdapter; import org.sigmah.offline.sync.SuccessCallback; import org.sigmah.shared.command.UpdateEntity; import org.sigmah.shared.command.result.Authentication; import org.sigmah.shared.command.result.Calendar; import org.sigmah.shared.command.result.VoidResult; import org.sigmah.shared.computation.Computation; import org.sigmah.shared.dto.ProjectDTO; import org.sigmah.shared.dto.ProjectFundingDTO; import org.sigmah.shared.dto.ProjectModelDTO; import org.sigmah.shared.dto.calendar.CalendarWrapper; import org.sigmah.shared.dto.calendar.Event; import org.sigmah.shared.dto.calendar.PersonalCalendarIdentifier; import org.sigmah.shared.dto.calendar.PersonalEventDTO; import org.sigmah.shared.dto.element.ComputationElementDTO; import org.sigmah.shared.dto.reminder.MonitoredPointDTO; import org.sigmah.shared.dto.reminder.ReminderDTO; import org.sigmah.shared.dto.report.KeyQuestionDTO; import org.sigmah.shared.dto.report.ProjectReportContent; import org.sigmah.shared.dto.report.ProjectReportDTO; import org.sigmah.shared.dto.report.ProjectReportSectionDTO; import org.sigmah.shared.dto.report.RichTextElementDTO; /** * JavaScript implementation of {@link org.sigmah.server.handler.UpdateEntityHandler}. * Used when the user is offline. * * @author Raphaƫl Calabro (rcalabro@ideia.fr) */ public class UpdateEntityAsyncHandler implements AsyncCommandHandler<UpdateEntity, VoidResult>, DispatchListener<UpdateEntity, VoidResult> { @Inject private UpdateDiaryAsyncDAO updateDiaryAsyncDAO; @Inject private ProjectReportAsyncDAO projectReportAsyncDAO; @Inject private PersonalCalendarAsyncDAO personalCalendarAsyncDAO; @Inject private ReminderAsyncDAO reminderAsyncDAO; @Inject private MonitoredPointAsyncDAO monitoredPointAsyncDAO; @Inject private ProjectAsyncDAO projectAsyncDAO; @Inject private ComputationAsyncDAO computationAsyncDAO; @Inject private ClientValueResolver valueResolver; @Inject private ValueAsyncDAO valueAsyncDAO; @Override public void execute(UpdateEntity command, OfflineExecutionContext executionContext, AsyncCallback<VoidResult> callback) { executeCommand(command, executionContext.getAuthentication(), callback); updateDiaryAsyncDAO.saveOrUpdate(command); } @Override public void onSuccess(UpdateEntity command, VoidResult result, Authentication authentication) { executeCommand(command, authentication, null); } private void executeCommand(UpdateEntity command, Authentication authentication, AsyncCallback<VoidResult> callback) { if (MonitoredPointDTO.ENTITY_NAME.equals(command.getEntityName())) { updateMonitoredPoint(command.getId(), command.getChanges(), callback); } else if(ProjectReportDTO.ENTITY_NAME.equals(command.getEntityName())) { updateProjectReport(command.getId(), command.getChanges(), authentication, callback); } else if(PersonalEventDTO.ENTITY_NAME.equals(command.getEntityName())) { updatePersonalEvent(command.getId(), command.getChanges(), callback); } else if(ProjectDTO.ENTITY_NAME.equals(command.getEntityName())) { updateProject(command.getId(), command.getChanges(), callback); } else if(ReminderDTO.ENTITY_NAME.equals(command.getEntityName())) { updateReminder(command.getId(), command.getChanges(), callback); } else if (ProjectFundingDTO.ENTITY_NAME.equals(command.getEntityName())) { updateProjectFunding(command.getId(), command.getChanges(), callback); } else { exception(command, callback != null); } } private void exception(UpdateEntity command, boolean throwException) throws UnsupportedOperationException { if (throwException) { throw new UnavailableCommandException("Update of type '" + command.getEntityName() + "' is not supported yet."); } } private void updateProjectReport(int entityId, final RpcMap changes, final Authentication authentication, final AsyncCallback<VoidResult> callback) { projectReportAsyncDAO.getByVersionId(entityId, new SuccessCallback<ProjectReportDTO>(callback) { @Override public void onSuccess(final ProjectReportDTO projectReportDTO) { for (final Map.Entry<String, Object> entry : changes.entrySet()) { if (ProjectReportDTO.CURRENT_PHASE.equals(entry.getKey())) { projectReportDTO.setPhaseName((String) entry.getValue()); projectReportDTO.setEditorName(authentication.getUserCompleteName()); projectReportDTO.setLastEditDate(new Date()); } else { final RichTextElementDTO richTextElement = findRichTextElement(Integer.valueOf(entry.getKey()), new ArrayList<ProjectReportContent>(projectReportDTO.getSections())); if(richTextElement != null) { richTextElement.setText((String) entry.getValue()); } } } projectReportAsyncDAO.saveOrUpdate(projectReportDTO, new AsyncAdapter<ProjectReportDTO, VoidResult>(callback)); } }); } private RichTextElementDTO findRichTextElement(final Integer id, final List<ProjectReportContent> contents) { for (final ProjectReportContent content : contents) { if (content instanceof RichTextElementDTO) { final RichTextElementDTO richTextElement = (RichTextElementDTO) content; if(id.equals(richTextElement.getId())) { return richTextElement; } } else if (content instanceof ProjectReportSectionDTO) { final ProjectReportSectionDTO section = (ProjectReportSectionDTO) content; final RichTextElementDTO value = findRichTextElement(id, section.getChildren()); if(value != null) { return value; } } else if (content instanceof KeyQuestionDTO) { final KeyQuestionDTO keyQuestion = (KeyQuestionDTO) content; if(id.equals(keyQuestion.getRichTextElementDTO().getId())) { return keyQuestion.getRichTextElementDTO(); } } } return null; } private void updatePersonalEvent(final int entityId, final RpcMap changes, final AsyncCallback<VoidResult> callback) { final CalendarWrapper calendarWrapper = (CalendarWrapper) changes.get(Event.CALENDAR_ID); final PersonalCalendarIdentifier personalCalendarIdentifier = (PersonalCalendarIdentifier) calendarWrapper.getCalendar().getIdentifier(); personalCalendarAsyncDAO.get(personalCalendarIdentifier.getId(), new SuccessCallback<Calendar>(callback) { @Override public void onSuccess(final Calendar calendar) { boolean done = false; final Iterator<Map.Entry<Date, List<Event>>> mapEntryIterator = calendar.getEvents().entrySet().iterator(); while (!done && mapEntryIterator.hasNext()) { final Map.Entry<Date, List<Event>> entry = mapEntryIterator.next(); final Iterator<Event> eventIterator = entry.getValue().iterator(); while (!done && eventIterator.hasNext()) { final Event event = eventIterator.next(); if (event.getIdentifier() != null && event.getIdentifier().equals(entityId)) { event.fillValues(changes.getTransientMap()); done = true; } } } personalCalendarAsyncDAO.saveOrUpdate(calendar, new AsyncAdapter<Calendar, VoidResult>(callback)); } }); } private void updateProject(final int entityId, final RpcMap changes, final AsyncCallback<VoidResult> callback) { if (changes.containsKey("dateDeleted")) { // Removes the project from the local database. projectAsyncDAO.remove(entityId, callback); } // TODO: Support "fundedId" and "fundingId" if necessary. } private void updateReminder(final int entityId, final RpcMap changes, final AsyncCallback<VoidResult> callback) { reminderAsyncDAO.get(entityId, new SuccessCallback<ReminderDTO>(callback) { @Override public void onSuccess(final ReminderDTO reminder) { reminder.setExpectedDate(new Date((Long) changes.get(ReminderDTO.EXPECTED_DATE))); reminder.setLabel((String) changes.get(ReminderDTO.LABEL)); final Boolean deleted = (Boolean) changes.get(ReminderDTO.DELETED); if (deleted != null) { reminder.setDeleted(deleted); } reminderAsyncDAO.saveOrUpdate(reminder, new AsyncAdapter<ReminderDTO, VoidResult>(callback)); } }); } private void updateMonitoredPoint(final int entityId, final RpcMap changes, final AsyncCallback<VoidResult> callback) { monitoredPointAsyncDAO.get(entityId, new SuccessCallback<MonitoredPointDTO>(callback) { @Override public void onSuccess(final MonitoredPointDTO point) { point.setExpectedDate(new Date((Long) changes.get(MonitoredPointDTO.EXPECTED_DATE))); point.setLabel((String) changes.get(MonitoredPointDTO.LABEL)); final Boolean deleted = (Boolean) changes.get(MonitoredPointDTO.DELETED); if (deleted != null) { point.setDeleted(deleted); } monitoredPointAsyncDAO.saveOrUpdate(point, new AsyncAdapter<MonitoredPointDTO, VoidResult>(callback)); } }); } private void updateProjectFunding(final int entityId, final RpcMap changes, final AsyncCallback<VoidResult> callback) { final RequestManager<VoidResult> requestManager = new RequestManager<VoidResult>(null, callback); projectAsyncDAO.getByProjectFundingId(entityId, new RequestManagerCallback<VoidResult, List<ProjectDTO>>(requestManager) { @Override public void onRequestSuccess(final List<ProjectDTO> projects) { final Double percentage = (Double) changes.get(ProjectFundingDTO.PERCENTAGE); updateProjectFunding(projects, entityId, percentage, requestManager); } }); requestManager.ready(); } private void updateProjectFunding(final List<ProjectDTO> projects, final int entityId, final Double percentage, final RequestManager<VoidResult> requestManager) { final ArrayList<ProjectFundingDTO> allFundings = new ArrayList<ProjectFundingDTO>(); for (final ProjectDTO project : projects) { allFundings.addAll(project.getFunded()); allFundings.addAll(project.getFunding()); } final ArrayList<ProjectFundingDTO> affectedFundings = new ArrayList<ProjectFundingDTO>(); for (final ProjectFundingDTO funding : allFundings) { if (funding.getId() == entityId) { funding.setPercentage(percentage); affectedFundings.add(funding); } } // Saving the changes. projectAsyncDAO.saveAll(projects, new RequestManagerCallback<VoidResult, Void>(requestManager) { @Override public void onRequestSuccess(Void result) { // Updating computations. computationAsyncDAO.get(true, new RequestManagerCallback<VoidResult, List<ComputationElementDTO>>(requestManager) { @Override public void onRequestSuccess(final List<ComputationElementDTO> computationElements) { updateComputationsValues(computationElements, affectedFundings, requestManager); } }); } }); } private void updateComputationsValues(final List<ComputationElementDTO> computationElements, final List<ProjectFundingDTO> affectedFundings, final RequestManager<VoidResult> requestManager) { for (final ComputationElementDTO computationElement : computationElements) { final ProjectModelDTO parentModel = computationElement.getProjectModel(); if (parentModel != null) { final Computation computation = computationElement.getComputationForModel(parentModel); final Integer parentModelId = parentModel.getId(); for (final ProjectFundingDTO projectFunding : affectedFundings) { final ProjectDTO fundedProject = projectFunding.getFunded(); if (parentModelId.equals(fundedProject.getProjectModel().getId())) { updateComputationValueForProject(computationElement, computation, fundedProject, requestManager); } final ProjectDTO fundingProject = projectFunding.getFunding(); if (parentModelId.equals(fundingProject.getProjectModel().getId())) { updateComputationValueForProject(computationElement, computation, fundingProject, requestManager); } } } } } /** * Update the value of the given computation for the given project. * * @param computationElement * Element to update. * @param computation * Formula of the computation. * @param project * Project to update. * @param requestManager * Request manager to use. */ private void updateComputationValueForProject(final ComputationElementDTO computationElement, final Computation computation, final ProjectDTO project, final RequestManager<VoidResult> requestManager) { computation.computeValueWithResolver(project.getId(), valueResolver, new RequestManagerCallback<VoidResult, String>(requestManager) { @Override public void onRequestSuccess(String result) { valueAsyncDAO.saveOrUpdate(result, computationElement, project.getId(), new RequestManagerCallback<VoidResult, VoidResult>(requestManager) { @Override public void onRequestSuccess(VoidResult result) { // Success. } }); } }); } }