package org.sigmah.server.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.google.inject.Inject; import java.util.ArrayList; import java.util.List; import javax.persistence.NoResultException; import javax.persistence.Query; import javax.persistence.TypedQuery; import org.apache.commons.lang3.StringUtils; import org.sigmah.server.dispatch.impl.UserDispatch.UserExecutionContext; import org.sigmah.server.domain.Amendment; import org.sigmah.server.domain.HistoryToken; import org.sigmah.server.domain.Project; import org.sigmah.server.domain.element.ComputationElement; import org.sigmah.server.domain.element.DefaultFlexibleElement; import org.sigmah.server.domain.element.FlexibleElement; import org.sigmah.server.file.FileStorageProvider; import org.sigmah.server.handler.base.AbstractCommandHandler; import org.sigmah.server.service.ComputationService; import org.sigmah.shared.command.GetValue; import org.sigmah.shared.command.result.ValueResult; import org.sigmah.shared.computation.value.ComputedValue; import org.sigmah.shared.computation.value.ComputedValues; import org.sigmah.shared.dispatch.CommandException; import org.sigmah.shared.dto.element.BudgetDistributionElementDTO; import org.sigmah.shared.dto.element.BudgetRatioElementDTO; import org.sigmah.shared.dto.element.ComputationElementDTO; import org.sigmah.shared.dto.element.DefaultFlexibleElementDTO; import org.sigmah.shared.dto.element.FilesListElementDTO; import org.sigmah.shared.dto.element.IndicatorsListElementDTO; import org.sigmah.shared.dto.element.MessageElementDTO; import org.sigmah.shared.dto.element.ReportListElementDTO; import org.sigmah.shared.dto.element.TripletsListElementDTO; import org.sigmah.shared.dto.report.ReportReference; import org.sigmah.shared.dto.value.BudgetPartsListValueDTO; import org.sigmah.shared.dto.value.FileDTO; import org.sigmah.shared.dto.value.FileVersionDTO; import org.sigmah.shared.dto.value.IndicatorsListValueDTO; import org.sigmah.shared.dto.value.ListableValue; import org.sigmah.shared.dto.value.TripletValueDTO; import org.sigmah.shared.util.ValueResultUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Handler getting the value of a {@link FlexibleElement}. * * @author Denis Colliot (dcolliot@ideia.fr) * @author Maxime Lombard (mlombard@ideia.fr) * @author Raphaƫl Calabro (rcalabro@ideia.fr) */ public class GetValueHandler extends AbstractCommandHandler<GetValue, ValueResult> { /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger(GetValueHandler.class); /** * Allow access to the files. */ @Inject private FileStorageProvider fileStorageProvider; /** * Service to compute values of ComputationElements. */ @Inject private ComputationService computationService; /** * Gets a flexible element value from the database. * * @param cmd * {@link GetValue} command containing the flexible element class, its id, and the project id * @return a {@link ValueResult} object containing the value of the flexible element or containing {@code null} if * there is no value defined for this element. * @throws org.sigmah.shared.dispatch.CommandException */ @Override public ValueResult execute(final GetValue cmd, final UserExecutionContext context) throws CommandException { LOG.debug("Getting value object from the database for command: '{}'.", cmd); // Command result. final ValueResult valueResult = new ValueResult(); // Amendment String historyValue = null; if (cmd.getAmendmentId() != null) { final TypedQuery<Amendment> amedmentQuery = em().createQuery("SELECT a FROM Amendment a WHERE a.id = :amendmentId", Amendment.class); amedmentQuery.setParameter("amendmentId", cmd.getAmendmentId()); final Amendment amendment = amedmentQuery.getSingleResult(); final List<HistoryToken> tokens = amendment.getValues(); if (tokens != null) { for (final HistoryToken token : tokens) { if (token.getElementId().equals(cmd.getElementId()) && token.getLayoutGroupIterationId() == cmd.getIterationId()) { historyValue = token.getValue(); } } } } // -------------------------------------------------------------------- // STEP 1 : gets the string value (regardless of the element). // -------------------------------------------------------------------- final String valueFromDatabase; if (DefaultFlexibleElementDTO.ENTITY_NAME.equals(cmd.getElementEntityName())) { valueFromDatabase = findCurrentValueOfDefaultElement(cmd.getProjectId(), cmd.getElementId()); } else if (BudgetRatioElementDTO.ENTITY_NAME.equals(cmd.getElementEntityName())) { valueFromDatabase = findCurrentValueOfBudgetRatioElement(cmd.getProjectId(), cmd.getElementId()); } else { valueFromDatabase = findCurrentValue(cmd.getProjectId(), cmd.getElementId(), cmd.getIterationId()); } String valueAsString = valueFromDatabase; boolean isValueExisting = valueFromDatabase != null; // Overriding the value by the old one if we have to display an amendment if (historyValue != null) { valueAsString = historyValue; isValueExisting = true; valueResult.setAmendment(true); } // No value exists for the flexible element. if (!isValueExisting) { LOG.debug("No value for this flexible element #{}.", cmd.getElementId()); if (ComputationElementDTO.ENTITY_NAME.equals(cmd.getElementEntityName())) { final ComputationElement computationElement = em().find(ComputationElement.class, cmd.getElementId()); final Project project = em().find(Project.class, cmd.getProjectId()); final ComputedValue computedValue = computationService.computeValueForProject(computationElement, project); valueResult.setValueObject(computedValue.toString()); } return valueResult; } // -------------------------------------------------------------------- // STEP 2 : gets the true values (depending of the element). // Can be a list of id with requires a sub-select query. // -------------------------------------------------------------------- Query query = null; String elementClassName = cmd.getElementEntityName(); ListableValue dto = null; Boolean isList = null; // Creates the sub-select query to get the true value. if (elementClassName.equals(TripletsListElementDTO.ENTITY_NAME)) { LOG.debug("Case TripletsListElementDTO."); dto = new TripletValueDTO(); isList = true; query = em().createQuery("SELECT tv FROM TripletValue tv WHERE tv.id IN (:idsList)"); query.setParameter("idsList", ValueResultUtils.splitValuesAsInteger(valueAsString)); } else if (elementClassName.equals(IndicatorsListElementDTO.ENTITY_NAME)) { LOG.debug("Case IndicatorsListElementDTO."); dto = new IndicatorsListValueDTO(); isList = true; query = em().createQuery("SELECT ilv FROM IndicatorsListValue ilv WHERE ilv.id.idList = :value"); query.setParameter("value", Integer.valueOf(valueAsString)); } else if (elementClassName.equals(BudgetDistributionElementDTO.ENTITY_NAME)) { LOG.debug("Case BudgetDistributionElementDTO."); dto = new BudgetPartsListValueDTO(); isList = true; query = em().createQuery("SELECT bplv FROM BudgetPartsListValue bplv WHERE bplv.id = :value"); query.setParameter("value", Integer.valueOf(valueAsString)); } else if (elementClassName.equals(FilesListElementDTO.ENTITY_NAME)) { LOG.debug("Case FilesListElementDTO."); dto = new FileDTO(); isList = true; query = em().createQuery("SELECT f FROM File f WHERE f.id IN (:idsList)"); query.setParameter("idsList", ValueResultUtils.splitValuesAsInteger(valueAsString)); } else if (elementClassName.equals(ReportListElementDTO.ENTITY_NAME)) { LOG.debug("Case ReportListElementDTO."); dto = new ReportReference(); isList = true; query = em().createQuery("SELECT r FROM ProjectReport r WHERE r.id IN (:idList)"); query.setParameter("idList", ValueResultUtils.splitValuesAsInteger(valueAsString)); } else if (!(elementClassName.equals(MessageElementDTO.ENTITY_NAME))) { LOG.debug("Case others (but MessageElementDTO)."); dto = null; isList = false; } // -------------------------------------------------------------------- // STEP 3 : fill the command result with the values. // -------------------------------------------------------------------- // No value for this kind of elements. if (isList == null) { return valueResult; } // Multiple results case if (isList) { LOG.debug("Multiple values for the element #{}.", cmd.getElementId()); @SuppressWarnings("unchecked") final List<Object> objectsList = query.getResultList(); final List<ListableValue> serializablesList = new ArrayList<>(); for (Object o : objectsList) { serializablesList.add(mapper().map(o, dto.getClass())); } if(elementClassName.equals(FilesListElementDTO.ENTITY_NAME)) { for(final ListableValue value : serializablesList) { if(value instanceof FileDTO) { final FileDTO file = (FileDTO)value; for(final FileVersionDTO version : file.getVersions()) { version.setAvailable(fileStorageProvider.exists(version.getPath())); } } } } valueResult.setValuesObject(serializablesList); } // Single result case else { LOG.debug("Single value for the element #{}.", cmd.getElementId()); // A single value is always interpreted as a string. valueResult.setValueObject(valueAsString); } LOG.debug("Returned value = {}.", valueResult); return valueResult; } private String findCurrentValue(int projectId, int elementId, Integer iterationId) { // Creates the query to get the value for the flexible element (as // string) in the Value table. final TypedQuery<String> valueQuery; if(iterationId == null) { valueQuery = em().createQuery("SELECT v.value FROM Value v WHERE v.containerId = :projectId AND v.element.id = :elementId", String.class); valueQuery.setParameter("projectId", projectId); valueQuery.setParameter("elementId", elementId); } else { valueQuery = em().createQuery("SELECT v.value FROM Value v WHERE v.element.id = :elementId AND v.layoutGroupIteration.id = :iterationId", String.class); valueQuery.setParameter("elementId", elementId); valueQuery.setParameter("iterationId", iterationId); } String valueAsString; // Executes the query and tests if a value exists for this flexible // element. try { valueAsString = valueQuery.getSingleResult(); if (StringUtils.isBlank(valueAsString)) { valueAsString = null; } } catch (NoResultException | ClassCastException e) { valueAsString = null; } return valueAsString; } private String findCurrentValueOfDefaultElement(int projectId, int elementId) { final Project container = em().find(Project.class, projectId); final DefaultFlexibleElement element = em().find(DefaultFlexibleElement.class, elementId); return element.getValue(container); } /** * Find in database the current value of a budget ratio element. * * @param containerId * @param elementId * @return */ private String findCurrentValueOfBudgetRatioElement(int containerId, int elementId) { // Selecting multiple columns results in an Object[] return type. // http://docs.oracle.com/cd/E17904_01/apirefs.1111/e13946/ejb3_langref.html#ejb3_langref_resulttype final TypedQuery<Object[]> valueQuery = em().createQuery("SELECT v1.value, v2.value " + "FROM BudgetRatioElement bre, Value v1, Value v2 " + "WHERE bre.id = :elementId " + "AND v1.containerId = :containerId AND v1.element = bre.spentBudget " + "AND v2.containerId = :containerId AND v2.element = bre.plannedBudget", Object[].class); valueQuery.setParameter("containerId", containerId); valueQuery.setParameter("elementId", elementId); // Executes the query and tests if a value exists for this flexible // element. try { final Object[] values = valueQuery.getSingleResult(); if (values != null && values.length == 2) { final ComputedValue spent = ComputedValues.from((String) values[0]); final ComputedValue planned = ComputedValues.from((String) values[1]); final Double valueAsDouble = planned.divide(spent).get(); if (valueAsDouble != null) { return valueAsDouble.toString(); } } } catch (NoResultException e) { // Ignored. } return null; } }