package org.sigmah.offline.dao; /* * #%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 java.util.ArrayList; import java.util.Collection; import java.util.List; import org.sigmah.offline.indexeddb.Cursor; import org.sigmah.offline.indexeddb.IDBKeyRange; import org.sigmah.offline.indexeddb.Index; import org.sigmah.offline.indexeddb.ObjectStore; import org.sigmah.offline.indexeddb.OpenCursorRequest; import org.sigmah.offline.indexeddb.Request; import org.sigmah.offline.indexeddb.Store; import org.sigmah.offline.indexeddb.Transaction; import org.sigmah.offline.js.ProjectFundingJS; import org.sigmah.offline.js.ProjectJS; import org.sigmah.offline.js.Values; import org.sigmah.shared.command.result.ListResult; import org.sigmah.shared.dto.PhaseDTO; import org.sigmah.shared.dto.ProjectDTO; import org.sigmah.shared.dto.ProjectFundingDTO; import org.sigmah.shared.dto.ProjectModelDTO; import org.sigmah.shared.dto.logframe.LogFrameDTO; import org.sigmah.shared.dto.orgunit.OrgUnitDTO; import org.sigmah.shared.dto.reminder.MonitoredPointDTO; import org.sigmah.shared.dto.reminder.ReminderDTO; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayInteger; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import com.google.inject.Singleton; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.sigmah.offline.indexeddb.Indexes; import org.sigmah.offline.js.ValueJSIdentifierFactory; import org.sigmah.shared.command.result.ValueResult; import org.sigmah.shared.dto.category.CategoryElementDTO; import org.sigmah.shared.dto.element.FlexibleElementDTO; import org.sigmah.shared.dto.element.QuestionChoiceElementDTO; import org.sigmah.shared.dto.element.QuestionElementDTO; import org.sigmah.shared.dto.referential.ElementTypeEnum; import org.sigmah.shared.util.ValueResultUtils; /** * Asynchronous DAO for saving and loading <code>ProjectDTO</code> objects. * * @author Raphaƫl Calabro (rcalabro@ideia.fr) * @author Denis Colliot (dcolliot@ideia.fr) */ @Singleton public class ProjectAsyncDAO extends AbstractUserDatabaseAsyncDAO<ProjectDTO, ProjectJS> { @Inject private ProjectModelAsyncDAO projectModelAsyncDAO; @Inject private OrgUnitAsyncDAO orgUnitAsyncDAO; @Inject private PhaseAsyncDAO phaseAsyncDAO; @Inject private LogFrameAsyncDAO logFrameAsyncDAO; @Inject private MonitoredPointAsyncDAO monitoredPointAsyncDAO; @Inject private ReminderAsyncDAO reminderAsyncDAO; @Inject private ValueAsyncDAO valueAsyncDAO; @Override public void saveOrUpdate(final ProjectDTO t, final AsyncCallback<ProjectDTO> callback, Transaction<Store> transaction) { super.saveOrUpdate(t, callback, transaction); // Saving the project model projectModelAsyncDAO.saveOrUpdate(t.getProjectModel(), null, transaction); // Saving phases phaseAsyncDAO.saveAll(t.getPhases(), null, transaction); // Saving the log frame logFrameAsyncDAO.saveOrUpdate(t.getLogFrame(), null, transaction); // Saving the monitored points and reminders monitoredPointAsyncDAO.saveOrUpdate(t.getPointsList(), transaction); reminderAsyncDAO.saveOrUpdate(t.getRemindersList(), transaction); } /** * {@inheritDoc} */ @Override public void get(final int id, final AsyncCallback<ProjectDTO> callback, final Transaction<Store> transaction) { get(id, true, callback, transaction); } private void get(final int id, final boolean loadChildren, final AsyncCallback<ProjectDTO> callback, final Transaction<Store> transaction) { if(loadChildren && transaction.useObjectFromCache(ProjectDTO.class, id, callback)) { return; } final ObjectStore projectStore = transaction.getObjectStore(Store.PROJECT); projectStore.get(id).addCallback(new AsyncCallback<Request>() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); } @Override public void onSuccess(Request request) { final ProjectJS projectJS = request.getResult(); if (projectJS != null) { final ProjectDTO projectDTO = projectJS.toDTO(); final RequestManager<ProjectDTO> requestManager = new RequestManager<ProjectDTO>(projectDTO, callback); loadProjectDTO(projectJS, loadChildren, requestManager, projectDTO, transaction); requestManager.ready(); } else { callback.onSuccess(null); } } }); } /** * Retrieves only the informations stored inside ProjectJS. * * @param id Identifier of the project to retrieve. * @param callback Handler to call when the search is done. */ public void getWithoutDependencies(final int id, final AsyncCallback<ProjectDTO> callback) { openTransaction(Transaction.Mode.READ_ONLY, new OpenTransactionHandler<Store>() { @Override public void onTransaction(Transaction<Store> transaction) { final ObjectStore projectStore = transaction.getObjectStore(getRequiredStore()); projectStore.get(id).addCallback(new AsyncCallback<Request>() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); } @Override public void onSuccess(Request request) { callback.onSuccess(request.getResult() != null ? request.<ProjectJS>getResult().toDTO() : null); } }); } }); } /** * Retrieves only the informations stored inside ProjectJS. * * @param indexName * Name of the index to use. * @param id * Indexed value to retrieve. * @param callback * Handler to call when the search is done. */ public void getByIndexWithoutDependencies(final String indexName, final int id, final AsyncCallback<ProjectDTO> callback) { openTransaction(Transaction.Mode.READ_ONLY, new OpenTransactionHandler<Store>() { @Override public void onTransaction(Transaction<Store> transaction) { final ObjectStore projectStore = transaction.getObjectStore(getRequiredStore()); projectStore.index(indexName).get(id).addCallback(new AsyncCallback<Request>() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); } @Override public void onSuccess(Request request) { callback.onSuccess(request.getResult() != null ? request.<ProjectJS>getResult().toDTO() : null); } }); } }); } /** * Returns the 2 projects associated with the given project funding id. * * @param id * Identifier of the project funding. * @param callback * Handler to call when the search is done. */ public void getByProjectFundingId(final int id, final AsyncCallback<List<ProjectDTO>> callback) { openTransaction(Transaction.Mode.READ_ONLY, new OpenTransactionHandler<Store>() { @Override public void onTransaction(final Transaction<Store> transaction) { getProjectsByIndex(Indexes.PROJECT_PROJECTFUNDINGS, id, callback, transaction); } }); } private void getProjectsByIndex(final String index, final int id, final AsyncCallback<List<ProjectDTO>> callback, final Transaction<Store> transaction) { final List<ProjectDTO> projects = new ArrayList<ProjectDTO>(); final RequestManager<List<ProjectDTO>> requestManager = new RequestManager<List<ProjectDTO>>(projects, callback); final ObjectStore projectStore = transaction.getObjectStore(getRequiredStore()); final OpenCursorRequest request = projectStore.index(index).openKeyCursor(IDBKeyRange.only(id)); final int cursorRequest = requestManager.prepareRequest(); request.addCallback(new AsyncCallback<Request>() { @Override public void onFailure(Throwable caught) { requestManager.setRequestFailure(cursorRequest, caught); } @Override public void onSuccess(Request result) { final Cursor cursor = request.getResult(); if (cursor != null) { final ProjectJS projectJS = cursor.getValue(); if (projectJS != null) { final ProjectDTO projectDTO = projectJS.toDTO(); projects.add(projectDTO); loadProjectDTO(projectJS, true, requestManager, projectDTO, transaction); } else { get(cursor.getPrimaryKey(), new RequestManagerCallback<List<ProjectDTO>, ProjectDTO>(requestManager) { @Override public void onRequestSuccess(final ProjectDTO project) { if (project != null) { projects.add(project); } } }, transaction); } cursor.next(); } else { requestManager.setRequestSuccess(cursorRequest); } } }); requestManager.ready(); } private void setChildrenProjects(final ProjectDTO project) { final ArrayList<ProjectDTO> children = new ArrayList<ProjectDTO>(); // Maps the funding projects. if (project.getFunding() != null) { for (final ProjectFundingDTO funding : project.getFunding()) { final ProjectDTO projectFunding = funding.getFunding(); if(projectFunding != null) { children.add(projectFunding); } } } // Maps the funded projects. if (project.getFunded() != null) { for (final ProjectFundingDTO funded : project.getFunded()) { final ProjectDTO projectFunded = funded.getFunded(); if (projectFunded != null) { children.add(projectFunded); } } } project.setChildrenProjects(children); } public void getProjectsByIds(final Collection<Integer> ids, final AsyncCallback<ListResult<ProjectDTO>> callback) { openTransaction(Transaction.Mode.READ_ONLY, new OpenTransactionHandler<Store>() { @Override public void onTransaction(Transaction<Store> transaction) { final ArrayList<ProjectDTO> projects = new ArrayList<ProjectDTO>(); final ListResult<ProjectDTO> projectListResult = new ListResult<ProjectDTO>(projects); final RequestManager<ListResult<ProjectDTO>> requestManager = new RequestManager<ListResult<ProjectDTO>>(projectListResult, callback); for(int id : ids) { get(id, new RequestManagerCallback<ListResult<ProjectDTO>, ProjectDTO>(requestManager) { @Override public void onRequestSuccess(ProjectDTO result) { projects.add(result); projectListResult.setSize(projects.size()); } }, transaction); } requestManager.ready(); } }); } public void getProjectsByOrgUnits(final Collection<Integer> orgUnitsIds, final AsyncCallback<ListResult<ProjectDTO>> callback) { openTransaction(Transaction.Mode.READ_ONLY, new OpenTransactionHandler<Store>() { @Override public void onTransaction(final Transaction<Store> transaction) { final ObjectStore projectStore = transaction.getObjectStore(Store.PROJECT); final Index orgUnitIndex = projectStore.index("orgUnit"); final int size = orgUnitsIds.size(); final int[] requests = new int[] {0}; final ArrayList<ProjectDTO> projects = new ArrayList<ProjectDTO>(); final ListResult<ProjectDTO> projectListResult = new ListResult<ProjectDTO>(projects); final RequestManager<ListResult<ProjectDTO>> requestManager = new RequestManager<ListResult<ProjectDTO>>(projectListResult, callback); for(final Integer orgUnitId : orgUnitsIds) { final OpenCursorRequest openCursorRequest = orgUnitIndex.openCursor(IDBKeyRange.only(orgUnitId.intValue())); openCursorRequest.addCallback(new RequestManagerCallback<ListResult<ProjectDTO>, Request>(requestManager) { @Override public void onRequestSuccess(Request request) { final Cursor cursor = openCursorRequest.getResult(); if(cursor != null) { final ProjectJS projectJS = (ProjectJS) cursor.getValue(); final ProjectDTO projectDTO = projectJS.toDTO(); final RequestManager<ProjectDTO> projectDTORequestManager = new RequestManager<ProjectDTO>(projectDTO, new RequestManagerCallback<ListResult<ProjectDTO>, ProjectDTO>(requestManager) { @Override public void onRequestSuccess(final ProjectDTO project) { projects.add(project); } }); loadProjectDTO(projectJS, true, projectDTORequestManager, projectDTO, transaction); projectDTORequestManager.ready(); cursor.next(); } else { requests[0]++; if(requests[0] == size) { requestManager.ready(); } } } }); } } }); } public void getIdsByOrgUnits(final Collection<Integer> orgUnitsIds, final AsyncCallback<ListResult<ProjectDTO>> callback) { openTransaction(Transaction.Mode.READ_ONLY, new OpenTransactionHandler<Store>() { @Override public void onTransaction(Transaction<Store> transaction) { final ObjectStore projectStore = transaction.getObjectStore(Store.PROJECT); final Index orgUnitIndex = projectStore.index("orgUnit"); final int size = orgUnitsIds.size(); final int[] requests = new int[] {0}; final ArrayList<ProjectDTO> projects = new ArrayList<ProjectDTO>(); for(final Integer orgUnitId : orgUnitsIds) { final OpenCursorRequest openCursorRequest = orgUnitIndex.openCursor(IDBKeyRange.only(orgUnitId.intValue())); openCursorRequest.addCallback(new AsyncCallback<Request>() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); } @Override public void onSuccess(Request request) { final Cursor cursor = openCursorRequest.getResult(); if(cursor != null) { final ProjectJS projectJS = (ProjectJS) cursor.getValue(); projects.add(projectJS.toDTO()); cursor.next(); } else { requests[0]++; if(requests[0] == size) { final ListResult<ProjectDTO> projectListResult = new ListResult<ProjectDTO>(projects); callback.onSuccess(projectListResult); } } } }); } } }); } private <M> void loadProjectDTO(final ProjectJS projectJS, final boolean loadChildren, final RequestManager<M> requestManager, final ProjectDTO projectDTO, final Transaction<Store> transaction) { // Loading categories final int categoriesRequest = requestManager.prepareRequest(); final RequestManager<ProjectDTO> categoryRequestManager = new RequestManager<ProjectDTO>(projectDTO, new RequestManagerCallback<M, ProjectDTO>(requestManager) { @Override public void onRequestSuccess(final ProjectDTO project) { project.setCategoryElements(new HashSet<CategoryElementDTO>()); for(final FlexibleElementDTO element : project.getProjectModel().getAllElements()) { if(element.getElementType() == ElementTypeEnum.QUESTION && ((QuestionElementDTO)element).getCategoryType() != null) { valueAsyncDAO.get(ValueJSIdentifierFactory.toIdentifier(element.getEntityName(), projectDTO.getId(), element.getId(), null), new RequestManagerCallback<M, ValueResult>(requestManager) { @Override public void onRequestSuccess(ValueResult result) { project.getCategoryElements().addAll(getCategoryElements(result, (QuestionElementDTO) element)); } }); } } requestManager.setRequestSuccess(categoriesRequest); } }); final int projectModelRequest = categoryRequestManager.prepareRequest(); categoryRequestManager.ready(); // Loading project model projectModelAsyncDAO.get(projectJS.getProjectModel(), new RequestManagerCallback<M, ProjectModelDTO>(requestManager) { @Override public void onRequestSuccess(ProjectModelDTO result) { projectDTO.setProjectModel(result); projectDTO.setVisibilities(result.getVisibilities()); categoryRequestManager.setRequestSuccess(projectModelRequest); if(loadChildren) { transaction.getObjectCache().put(projectDTO.getId(), projectDTO); final RequestManager<Void> fundedsAndFundingsRequestManager = new RequestManager<Void>(null, new RequestManagerCallback<M, Void>(requestManager) { @Override public void onRequestSuccess(Void result) { // Sets children projects. setChildrenProjects(projectDTO); } }); // Loading related projects final JsArray<ProjectFundingJS> fundeds = projectJS.getFunded(); if(fundeds != null) { final ArrayList<ProjectFundingDTO> dtos = new ArrayList<ProjectFundingDTO>(); projectDTO.setFunded(dtos); loadProjectFundings(fundeds, dtos, fundedsAndFundingsRequestManager, transaction); } final JsArray<ProjectFundingJS> fundings = projectJS.getFunding(); if(fundings != null) { final ArrayList<ProjectFundingDTO> dtos = new ArrayList<ProjectFundingDTO>(); projectDTO.setFunding(dtos); loadProjectFundings(fundings, dtos, fundedsAndFundingsRequestManager, transaction); } fundedsAndFundingsRequestManager.ready(); } } }, transaction); // Loading phases if (projectJS.getPhases() != null) { final ArrayList<PhaseDTO> phases = new ArrayList<PhaseDTO>(); projectDTO.setPhases(phases); final JsArrayInteger phaseIds = projectJS.getPhases(); final int size = phaseIds.length(); for(int index = 0; index < size; index++) { phaseAsyncDAO.get(phaseIds.get(index), new RequestManagerCallback<M, PhaseDTO>(requestManager) { @Override public void onRequestSuccess(PhaseDTO result) { result.setParentProject(projectDTO); phases.add(result); } }, transaction); } } if (Values.isDefined(projectJS, "currentPhase")) { phaseAsyncDAO.get(projectJS.getCurrentPhase(), new RequestManagerCallback<M, PhaseDTO>(requestManager) { @Override public void onRequestSuccess(PhaseDTO result) { projectDTO.setCurrentPhase(result); projectDTO.setCurrentPhaseName(result.getPhaseModel().getName()); } }, transaction); } // Loading log frame if (Values.isDefined(projectJS, "logFrame")) { logFrameAsyncDAO.get(projectJS.getLogFrame(), new RequestManagerCallback<M, LogFrameDTO>(requestManager) { @Override public void onRequestSuccess(LogFrameDTO result) { projectDTO.setLogFrame(result); } }, transaction); } // Loading monitored points and reminders if (projectDTO.getPointsList() != null && projectDTO.getPointsList().getId() != null) { monitoredPointAsyncDAO.getAllByParentListId(projectDTO.getPointsList().getId(), new RequestManagerCallback<M, List<MonitoredPointDTO>>(requestManager) { @Override public void onRequestSuccess(List<MonitoredPointDTO> result) { projectDTO.getPointsList().setPoints(result); } }, transaction); } if (projectDTO.getRemindersList() != null && projectDTO.getRemindersList().getId() != null) { reminderAsyncDAO.getAllByParentListId(projectDTO.getRemindersList().getId(), new RequestManagerCallback<M, List<ReminderDTO>>(requestManager) { @Override public void onRequestSuccess(List<ReminderDTO> result) { projectDTO.getRemindersList().setReminders(result); } }, transaction); } // Loading OrgUnit name. if (Values.isDefined(projectJS, "orgUnit")) { orgUnitAsyncDAO.get(projectJS.getOrgUnit(), new RequestManagerCallback<M, OrgUnitDTO>(requestManager) { @Override public void onRequestSuccess(OrgUnitDTO result) { if (result != null) { projectDTO.setOrgUnitName(result.getName() + " - " + result.getFullName()); } } }, transaction); } } private Set<CategoryElementDTO> getCategoryElements(ValueResult valueResult, QuestionElementDTO questionElement) { if (valueResult != null && valueResult.isValueDefined()) { if(questionElement.getMultiple() == null || !questionElement.getMultiple()) { final String idChoice = valueResult.getValueObject(); for (final QuestionChoiceElementDTO choice : questionElement.getChoices()) { if (idChoice.equals(String.valueOf(choice.getId()))) { return Collections.<CategoryElementDTO>singleton(choice.getCategoryElement()); } } } else { final Set<Integer> selectedChoicesId = new HashSet<Integer>(ValueResultUtils.splitValuesAsInteger(valueResult.getValueObject())); final Set<CategoryElementDTO> elements = new HashSet<CategoryElementDTO>(); for (final QuestionChoiceElementDTO choice : questionElement.getChoices()) { if (selectedChoicesId.contains(choice.getId())) { elements.add(choice.getCategoryElement()); } } return elements; } } return Collections.<CategoryElementDTO>emptySet(); } private <M> void loadProjectFundings(final JsArray<ProjectFundingJS> projectFundings, final List<ProjectFundingDTO> dtos, final RequestManager<M> requestManager, final Transaction<Store> transaction) { for(int index = 0; index < projectFundings.length(); index++) { final ProjectFundingJS projectFundingJS = projectFundings.get(index); final ProjectFundingDTO dto = projectFundingJS.toDTO(); dtos.add(dto); get(projectFundingJS.getFunded(), false, new RequestManagerCallback<M, ProjectDTO>(requestManager) { @Override public void onRequestSuccess(ProjectDTO result) { dto.setFunded(result); } }, transaction); get(projectFundingJS.getFunding(), false, new RequestManagerCallback<M, ProjectDTO>(requestManager) { @Override public void onRequestSuccess(ProjectDTO result) { dto.setFunding(result); } }, transaction); } } /** * {@inheritDoc} */ @Override public Store getRequiredStore() { return Store.PROJECT; } /** * {@inheritDoc} */ @Override public Collection<BaseAsyncDAO<Store>> getDependencies() { final ArrayList<BaseAsyncDAO<Store>> list = new ArrayList<BaseAsyncDAO<Store>>(); list.add(projectModelAsyncDAO); list.add(orgUnitAsyncDAO); list.add(phaseAsyncDAO); list.add(logFrameAsyncDAO); list.add(monitoredPointAsyncDAO); list.add(reminderAsyncDAO); return list; } /** * {@inheritDoc} */ @Override public ProjectJS toJavaScriptObject(ProjectDTO t) { return ProjectJS.toJavaScript(t); } /** * {@inheritDoc} */ @Override public ProjectDTO toJavaObject(ProjectJS js) { return js.toDTO(); } }