package org.sigmah.server.servlet.importer; /* * #%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.Injector; import org.sigmah.server.dispatch.impl.UserDispatch; import org.sigmah.server.domain.Country; import org.sigmah.server.domain.OrgUnit; import org.sigmah.server.domain.OrgUnitModel; import org.sigmah.server.domain.Project; import org.sigmah.server.domain.ProjectModel; import org.sigmah.server.domain.User; import org.sigmah.server.domain.base.EntityId; import org.sigmah.server.domain.element.CheckboxElement; import org.sigmah.server.domain.element.DefaultContactFlexibleElement; import org.sigmah.server.domain.element.DefaultFlexibleElement; import org.sigmah.server.domain.element.FlexibleElement; import org.sigmah.server.domain.element.QuestionChoiceElement; import org.sigmah.server.domain.element.QuestionElement; import org.sigmah.server.domain.element.TextAreaElement; import org.sigmah.server.domain.element.TripletsListElement; import org.sigmah.server.domain.importation.ImportationSchemeModel; import org.sigmah.server.domain.importation.Variable; import org.sigmah.server.domain.importation.VariableBudgetElement; import org.sigmah.server.domain.importation.VariableBudgetSubField; import org.sigmah.server.domain.importation.VariableFlexibleElement; import org.sigmah.server.i18n.I18nServer; import org.sigmah.server.mapper.Mapper; import org.sigmah.server.servlet.exporter.utils.ExporterUtil; import org.sigmah.shared.Language; import org.sigmah.shared.command.GetValue; import org.sigmah.shared.command.result.ValueResult; import org.sigmah.shared.dispatch.CommandException; import org.sigmah.shared.dispatch.FunctionalException; import org.sigmah.shared.dto.ElementExtractedValue; import org.sigmah.shared.dto.ImportDetails; import org.sigmah.shared.dto.ProjectDTO; import org.sigmah.shared.dto.base.EntityDTO; import org.sigmah.shared.dto.element.BudgetElementDTO; import org.sigmah.shared.dto.element.BudgetRatioElementDTO; import org.sigmah.shared.dto.element.CheckboxElementDTO; import org.sigmah.shared.dto.element.ComputationElementDTO; import org.sigmah.shared.dto.element.ContactListElementDTO; import org.sigmah.shared.dto.element.CoreVersionElementDTO; import org.sigmah.shared.dto.element.DefaultFlexibleElementDTO; import org.sigmah.shared.dto.element.FilesListElementDTO; import org.sigmah.shared.dto.element.FlexibleElementDTO; import org.sigmah.shared.dto.element.IndicatorsListElementDTO; import org.sigmah.shared.dto.element.MessageElementDTO; import org.sigmah.shared.dto.element.QuestionElementDTO; import org.sigmah.shared.dto.element.ReportElementDTO; import org.sigmah.shared.dto.element.ReportListElementDTO; import org.sigmah.shared.dto.element.TextAreaElementDTO; import org.sigmah.shared.dto.element.TripletsListElementDTO; import org.sigmah.shared.dto.importation.ImportationSchemeDTO; import org.sigmah.shared.dto.orgunit.OrgUnitDTO; import org.sigmah.shared.dto.referential.AmendmentState; import org.sigmah.shared.dto.referential.DefaultFlexibleElementType; import org.sigmah.shared.dto.referential.ElementExtractedValueStatus; import org.sigmah.shared.dto.referential.ImportStatusCode; import org.sigmah.shared.dto.referential.ImportationSchemeFileFormat; import org.sigmah.shared.dto.referential.ImportationSchemeImportType; import org.sigmah.shared.dto.referential.LogicalElementType; import org.sigmah.shared.util.Collections; import org.sigmah.shared.util.ValueResultUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Class for importing datas from spreadsheet and CSV documents * * @author Guerline Jean-Baptiste (gjbaptiste@ideia.fr) * @author Raphaƫl Calabro (rcalabro@ideia.fr) v2.0 */ public abstract class Importer implements Iterator<ImportDetails> { private static final Logger LOGGER = LoggerFactory.getLogger(Importer.class); private UserDispatch.UserExecutionContext executionContext; private Injector injector; protected ImportationSchemeDTO scheme; private Iterator<ImportationSchemeModel> schemeModels; private ImportationSchemeModel schemeModel; private Mapper mapper; private Language language; private I18nServer translator; /** * Sets the stream that will be parsed by this importer. * * @param inputStream * Stream to parse. * @throws java.io.IOException * If the file format is invalid or if an error occurs while reading from the stream. */ public abstract void setInputStream(InputStream inputStream) throws IOException; /** * Get the String value of the corresponding variable reference from the * imported file. * * @param reference * Location where to find the variable value (column number, cell reference, etc.) * @param lineNumber * Line number. * @param sheetName * Name of the sheet. * @return The value at the requested location. * @throws org.sigmah.shared.dispatch.FunctionalException */ public abstract Object getValueFromVariable(String reference, Integer lineNumber, String sheetName) throws FunctionalException; /** * Prepare the importer. Must be called before trying to parse an input * stream. * <p> * The attributes <code>schemeModelList</code>, * <code>entitiesToImport</code>, <code>language</code>, <code>mapper</code> * and <code>translator</code> will be initialized after this method. * <p> * The attributes <code>injector</code>, <code>executionContext</code> and * <code>executionContext</code> must be initialized before calling this * method. * * @throws CommandException * If an error occurs while retrieving the scheme models. * @throws IllegalStateException * If one of <code>injector</code>, <code>executionContext</code>, * <code>executionContext</code> is <code>null</code>. */ public void initialize() throws CommandException, IllegalStateException { if (injector == null || executionContext == null || scheme == null) { throw new IllegalStateException("injector, executionContext and scheme must be set before calling initialize."); } this.language = executionContext.getLanguage(); this.mapper = injector.getInstance(Mapper.class); this.translator = injector.getInstance(I18nServer.class); this.schemeModels = em().createQuery("SELECT ism FROM ImportationSchemeModel AS ism WHERE ism.importationScheme.id = :schemeId", ImportationSchemeModel.class) .setParameter("schemeId", scheme.getId()) .getResultList() .iterator(); } /** * Retrieves the detail of everything that can be imported. * * @return the entities to import. * @throws CommandException * If an error occurs during the analysis. */ public List<ImportDetails> getCorrespondances() throws CommandException { final ArrayList<ImportDetails> correspondances = new ArrayList<>(); while (hasNext()) { final ImportDetails importDetails = next(); if (importDetails != null) { correspondances.add(importDetails); } } return correspondances; } /** * Sets the instance of the Guice injecto. * * @param injector * Guice injector. */ public void setInjector(Injector injector) { this.injector = injector; } /** * Retrieves the current execution context. * * @return the current execution context. */ UserDispatch.UserExecutionContext getExecutionContext() { return executionContext; } /** * Sets the current execution context. * * @param executionContext * Execution context. */ public void setExecutionContext(UserDispatch.UserExecutionContext executionContext) { this.executionContext = executionContext; } /** * Sets the importation scheme to use. * * @param scheme * Importation scheme. */ public void setScheme(ImportationSchemeDTO scheme) { this.scheme = scheme; } /** * Retrieves the shared instance of translator. * * @return the shared instance of translator. */ I18nServer getTranslator() { return translator; } /** * {@inheritDoc} */ @Override public void remove() { throw new UnsupportedOperationException("Not supported."); } protected boolean hasNextSchemeModel() { return schemeModels.hasNext(); } protected ImportationSchemeModel nextSchemeModel() { this.schemeModel = schemeModels.next(); return schemeModel; } protected ImportationSchemeModel getSchemeModel() { return schemeModel; } /** * Get the map mapping a variable value and a flexible element value. * * @param variableFlexibleElementsDTO * @param lineNumber * number of the line * @param sheetName * name of the sheet * @return Map linking a flexible element to the value extracted from the * document * @throws Throwable */ private List<ElementExtractedValue> getCorrespondancesVariableFlexibleElement( List<VariableFlexibleElement> variableFlexibleElements, EntityId<Integer> entity, Integer lineNumber, String sheetName) { final List<ElementExtractedValue> correspondances = new ArrayList<ElementExtractedValue>(); for (VariableFlexibleElement variableFlexibleElement : variableFlexibleElements) { // FEATURE #789: Removed the identification key from the importation results. if (entity != null && variableFlexibleElement.getIsKey() != null && variableFlexibleElement.getIsKey()) { continue; } try { ElementExtractedValue elementExtractedValue = new ElementExtractedValue(); Object cellValue = null; if (variableFlexibleElement instanceof VariableBudgetElement) { // Handling budget elements. final VariableBudgetElement variableBudgetElement = (VariableBudgetElement) variableFlexibleElement; for (VariableBudgetSubField variableBudgetSubField : variableBudgetElement.getVariableBudgetSubFields()) { cellValue = getValueFromVariable(variableBudgetSubField.getVariable().getReference(), lineNumber, sheetName); Object[] valueStatus = getValueFormatForFlexibleElement(variableFlexibleElement.getFlexibleElement(), cellValue); if(valueStatus[0] != null) { elementExtractedValue.getNewBudgetValues().put(variableBudgetSubField.getBudgetSubField().getId(), (Serializable) valueStatus[0]); } if(valueStatus[1] != null) { elementExtractedValue.setStatus((ElementExtractedValueStatus) valueStatus[1]); } if (entity != null) { elementExtractedValue.setOldBudgetValues(getBudgetElementValue(variableFlexibleElement.getFlexibleElement(), entity)); } } } else { // Handling others elements. cellValue = getValueFromVariable(variableFlexibleElement.getVariable().getReference(), lineNumber, sheetName); Object[] valueStatus = getValueFormatForFlexibleElement(variableFlexibleElement.getFlexibleElement(), cellValue); if(valueStatus[0] != null) { elementExtractedValue.setNewValue((Serializable) valueStatus[0]); } if(valueStatus[1] != null) { elementExtractedValue.setStatus((ElementExtractedValueStatus) valueStatus[1]); } } final FlexibleElementDTO elementDTO = toDTO(variableFlexibleElement.getFlexibleElement()); elementExtractedValue.setElement(elementDTO); if (entity != null) { elementExtractedValue.setOldValue(getFlexibleElementValue(variableFlexibleElement.getFlexibleElement(), entity)); } correspondances.add(elementExtractedValue); } catch (FunctionalException e) { LOGGER.trace("An exception occured while retrieveing the value of the variable '" + variableFlexibleElement.getVariable().getName() + "' for the container '" + entity + "'", e); } } return correspondances; } /** * Gets the alphabetic position of the provided character (-1) * * @param letter * @return */ public Integer getNumericValuefromCharacter(Character letter) { if (letter >= 'A' && letter <= 'Z') return ((int) letter - 'A'); if (letter >= 'a' && letter <= 'z') return ((int) letter - 'a'); return null; } /** * Get the value of the flexible Element. * * @param flexibleElement * @param entityDTO * @param forkey * @return * @throws CommandException */ private Serializable getFlexibleElementValue(FlexibleElement flexibleElement, EntityId<Integer> entity) { final GetValue command = new GetValue(entity.getId(), flexibleElement.getId(), "element." + flexibleElement.getClass().getSimpleName(), null); final ValueResult valueResult; try { valueResult = executionContext.execute(command); } catch (CommandException e) { LOGGER.error("An error occured when trying to find the value of flexible element " + flexibleElement + " for the container " + entity, e); return null; } final FlexibleElement element; final Serializable valueObject; final LogicalElementType type = flexibleElement.type(); switch (type.toElementTypeEnum()) { case CHECKBOX: element = mapper.map(flexibleElement, new CheckboxElement()); valueObject = getCheckboxValue(valueResult, element); break; case CONTACT_LIST: valueObject = getContactListValue(valueResult); break; case DEFAULT: element = mapper.map(flexibleElement, new DefaultFlexibleElement()); if (!DefaultFlexibleElementType.BUDGET.equals(((DefaultFlexibleElement) element).type())) { valueObject = (Serializable) ExporterUtil.getDefElementPair(valueResult, element, entity, entity.getClass(), em(), translator, language).getValue(); } else { valueObject = null; } break; case DEFAULT_CONTACT: element = mapper.map(flexibleElement, new DefaultContactFlexibleElement()); if (!DefaultFlexibleElementType.BUDGET.equals(((DefaultContactFlexibleElement) element).type())) { valueObject = (Serializable) ExporterUtil.getDefElementPair(valueResult, element, entity, entity.getClass(), em(), translator, language).getValue(); } else { valueObject = null; } break; case QUESTION: element = mapper.map(flexibleElement, new QuestionElement()); valueObject = (Serializable) ExporterUtil.getChoicePair(element, valueResult).getValue(); break; case TEXT_AREA: element = mapper.map(flexibleElement, new TextAreaElement()); valueObject = (Serializable) ExporterUtil.getTextAreaElementPair(valueResult, element).getValue(); break; case TRIPLETS: element = mapper.map(flexibleElement, new TripletsListElement()); valueObject = (Serializable) ExporterUtil.getTripletPair(element, valueResult).getValue(); break; default: valueObject = null; break; } return valueObject; } private Map<Integer, String> getBudgetElementValue(FlexibleElement flexibleElement, EntityId<Integer> entity) { try { final String value = em().createQuery("SELECT v.value FROM Value AS v WHERE v.containerId = :containerId AND v.element = :element", String.class) .setParameter("containerId", entity.getId()) .setParameter("element", flexibleElement) .getSingleResult(); return ValueResultUtils.splitMapElements(value); } catch (NoResultException e) { LOGGER.trace("No value for the element " + flexibleElement + " of entity " + entity.getId(), e); return java.util.Collections.<Integer, String>emptyMap(); } } /** * Build the resultMap for the importationSchemeModel provided * * @param schemeModelDTO * @param lineNumber * number of line * @param sheetName * name of the sheet * @throws CommandException */ public ImportDetails getCorrespondancePerSheetOrLine(Integer lineNumber, String sheetName) { final VariableFlexibleElement keyVariableElement = getKeyOfCurrentSchemeModel(); if (keyVariableElement == null) { return null; } final ImportDetails importEntity = new ImportDetails(); final Variable variable = keyVariableElement.getVariable(); final FlexibleElement flexibleElement = keyVariableElement.getFlexibleElement(); final String keyValue; try { final Object cellValue = getValueFromVariable(variable.getReference(), lineNumber, sheetName); if (cellValue == null) { return null; } keyValue = cellValue.toString(); } catch (FunctionalException e) { LOGGER.error("An error occured while retrieving the value of the key.", e); return null; } LOGGER.debug("Key identification is " + keyValue); final String label = ExporterUtil.getFlexibleElementLabel(flexibleElement, translator, language); final Map<EntityDTO<Integer>, List<ElementExtractedValue>> entityCorrespondances = new HashMap<>(); // Checks if the model is an orgUnit or a project model if (schemeModel.getOrgUnitModel() != null) { final OrgUnitModel orgUnitModel = schemeModel.getOrgUnitModel(); LOGGER.debug("Import for org unit model : " + orgUnitModel.getName()); importEntity.setModelName(orgUnitModel.getName()); importEntity.setModelStatus(orgUnitModel.getStatus()); // Get all the orgUnits from an orgUnit model final List<OrgUnit> orgUnits = em().createQuery("SELECT ou FROM OrgUnit as ou WHERE ou.orgUnitModel = :orgUnitModel", OrgUnit.class) .setParameter("orgUnitModel", orgUnitModel) .getResultList(); // For each project get the value of the corresponding // identification key for (OrgUnit orgUnit : orgUnits) { final String valueString = (String) getFlexibleElementValue(flexibleElement, orgUnit); if (valueString != null && valueString.equals(keyValue)) { final List<ElementExtractedValue> correspondances = getCorrespondancesVariableFlexibleElement( schemeModel.getVariableFlexibleElements(), orgUnit, lineNumber, sheetName); final OrgUnitDTO orgUnitDTO = mapper.map(orgUnit, new OrgUnitDTO()); entityCorrespondances.put(orgUnitDTO, correspondances); } } // Initializes the importEntity according to the number of // orgUnits found importEntity.setKeyIdentification(label + " : " + keyValue); if (entityCorrespondances.isEmpty()) { importEntity.setEntityStatus(ImportStatusCode.ORGUNIT_NOT_FOUND_CODE); List<ElementExtractedValue> correspondances = getCorrespondancesVariableFlexibleElement( schemeModel.getVariableFlexibleElements(), null, lineNumber, sheetName); OrgUnitDTO o = new OrgUnitDTO(); o.setId(0); entityCorrespondances.put(o, correspondances); } else if (entityCorrespondances.size() == 1) { importEntity.setEntityStatus(ImportStatusCode.ORGUNIT_FOUND_CODE); } else { importEntity.setEntityStatus(ImportStatusCode.SEVERAL_ORGUNITS_FOUND_CODE); } importEntity.setEntitiesToImport(entityCorrespondances); } else if (schemeModel.getProjectModel() != null) { final Map<EntityDTO<Integer>, List<ElementExtractedValue>> lockedEntityCorrespondances = new HashMap<EntityDTO<Integer>, List<ElementExtractedValue>>(); final ProjectModel projectModel = schemeModel.getProjectModel(); LOGGER.debug("Import for project model : " + projectModel.getName()); importEntity.setModelName(projectModel.getName()); importEntity.setModelStatus(projectModel.getStatus()); // Get all the projects of a project model final List<Project> projects = em().createQuery("SELECT p FROM Project as p WHERE p.projectModel = :projectModel", Project.class) .setParameter("projectModel", projectModel) .getResultList(); // For each project get the value of the corresponding // identification key for (Project project : projects) { final String valueString = (String) getFlexibleElementValue(flexibleElement, project); if (valueString != null && valueString.equals(keyValue)) { final List<ElementExtractedValue> correspondances = getCorrespondancesVariableFlexibleElement( schemeModel.getVariableFlexibleElements(), project, lineNumber, sheetName); final ProjectDTO projectDTO = mapper.map(project, new ProjectDTO()); if (project.getAmendmentState() != null && project.getAmendmentState() == AmendmentState.LOCKED) { if (lockedEntityCorrespondances.isEmpty()) { lockedEntityCorrespondances.put(projectDTO, correspondances); } } else { entityCorrespondances.put(projectDTO, correspondances); } } } // Initializes the importEntity according to the number of // projects found importEntity.setKeyIdentification(label + " : " + keyValue); if (entityCorrespondances.isEmpty()) { if (!lockedEntityCorrespondances.isEmpty()) { importEntity.setEntityStatus(ImportStatusCode.PROJECT_LOCKED_CODE); importEntity.setEntitiesToImport(lockedEntityCorrespondances); } else { importEntity.setEntityStatus(ImportStatusCode.PROJECT_NOT_FOUND_CODE); List<ElementExtractedValue> correspondances = getCorrespondancesVariableFlexibleElement( schemeModel.getVariableFlexibleElements(), null, lineNumber, sheetName); ProjectDTO p = new ProjectDTO(); p.setId(0); entityCorrespondances.put(p, correspondances); importEntity.setEntitiesToImport(entityCorrespondances); } } else if (entityCorrespondances.size() == 1) { importEntity.setEntityStatus(ImportStatusCode.PROJECT_FOUND_CODE); importEntity.setEntitiesToImport(entityCorrespondances); } else { importEntity.setEntityStatus(ImportStatusCode.SEVERAL_PROJECTS_FOUND_CODE); importEntity.setEntitiesToImport(entityCorrespondances); } } return importEntity; } /** * Gets the right format of the value for the flexible element provided * * @param flexibleElement * @param value * @return */ public Object[] getValueFormatForFlexibleElement(FlexibleElement flexibleElement, Object value) { Object[] valueStatus = new Object[2]; Serializable formattedValue = null; ElementExtractedValueStatus statusCode = null; String stringValue = String.valueOf(value); if (value != null) { final LogicalElementType type = flexibleElement.type(); switch (type.toElementTypeEnum()) { case CHECKBOX: if (value instanceof Boolean) { formattedValue = (Serializable) value; statusCode = ElementExtractedValueStatus.VALID_VALUE; } else if (value instanceof String) { final String noValue = translator.t(language, "no"); final String yesValue = translator.t(language, "yes"); if ("true".equalsIgnoreCase(stringValue) || "false".equalsIgnoreCase(stringValue)) { formattedValue = Boolean.valueOf(stringValue); statusCode = ElementExtractedValueStatus.VALID_VALUE; } else if (noValue.equalsIgnoreCase(stringValue)) { formattedValue = false; statusCode = ElementExtractedValueStatus.VALID_VALUE; } else if (yesValue.equalsIgnoreCase(stringValue)) { formattedValue = true; statusCode = ElementExtractedValueStatus.VALID_VALUE; } } break; case DEFAULT: if (!stringValue.isEmpty()) { switch (type.toDefaultFlexibleElementType()) { case START_DATE: case END_DATE: if (value instanceof Number) { final Long time = Double.valueOf(stringValue).longValue(); formattedValue = new Date(time); statusCode = ElementExtractedValueStatus.VALID_VALUE; } else if (value instanceof Date) { formattedValue = (Date) value; statusCode = ElementExtractedValueStatus.VALID_VALUE; }else if (value instanceof String) { final SimpleDateFormat defaultFormat = new SimpleDateFormat("dd/MM/yy"); try { formattedValue = defaultFormat.parse(stringValue); statusCode = ElementExtractedValueStatus.VALID_VALUE; } catch (ParseException e) { statusCode = ElementExtractedValueStatus.INVALID_DATE_VALUE; } } break; case BUDGET: if(value instanceof String) { try{ formattedValue = Double.valueOf(stringValue); statusCode = ElementExtractedValueStatus.VALID_VALUE; } catch(NumberFormatException nfe) { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } } else if (value instanceof Number) { formattedValue = ((Number)value).doubleValue(); statusCode = ElementExtractedValueStatus.VALID_VALUE; } else { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } break; case ORG_UNIT: if (value instanceof String) { try { final int orgUnitId = Integer.parseInt(stringValue); value = orgUnitId; } catch( NumberFormatException e) { // Ignored. } // Searching by code and by name. final TypedQuery<OrgUnit> query = em().createQuery("SELECT o FROM OrgUnit o WHERE LOWER(o.name) = :value OR LOWER(o.fullName) = :value", OrgUnit.class); query.setParameter("value", stringValue.toLowerCase()); final List<OrgUnit> results = query.getResultList(); if (!results.isEmpty()) { // Selecting the first result. formattedValue = results.get(0).getId(); statusCode = ElementExtractedValueStatus.VALID_VALUE; } } if (value instanceof Integer) { // Searching by ID. final int orgUnitId = (Integer) value; final OrgUnit orgUnit = em().getReference(OrgUnit.class, orgUnitId); if (orgUnit != null) { statusCode = ElementExtractedValueStatus.VALID_VALUE; formattedValue = orgUnitId; } } if (formattedValue == null) { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } break; case COUNTRY: // TODO: Needs to prevent import of countries for projects. if (value instanceof String) { try { final int countryId = Integer.parseInt(stringValue); value = countryId; } catch( NumberFormatException e) { // Ignored. } // Searching by code ISO and by name. final TypedQuery<Country> query = em().createQuery("SELECT c FROM Country c WHERE LOWER(c.codeISO) = :value OR LOWER(c.name) = :value", Country.class); query.setParameter("value", stringValue.toLowerCase()); final List<Country> results = query.getResultList(); if (!results.isEmpty()) { // Selecting the first result. formattedValue = results.get(0).getId(); statusCode = ElementExtractedValueStatus.VALID_VALUE; } } if (value instanceof Integer) { // Searching by ID. final int countryId = (Integer) value; final Country country = em().getReference(Country.class, countryId); if (country != null) { statusCode = ElementExtractedValueStatus.VALID_VALUE; formattedValue = countryId; } } if (formattedValue == null) { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } break; case MANAGER: case OWNER: if (value instanceof String) { try { final int userId = Integer.parseInt(stringValue); value = userId; } catch( NumberFormatException e) { // Ignored. } // Searching by e-mail address and by last name. final TypedQuery<User> query = em().createQuery("SELECT o FROM User o WHERE LOWER(o.email) = :value OR LOWER(o.name) = :value", User.class); query.setParameter("value", stringValue.toLowerCase()); final List<User> results = query.getResultList(); if (!results.isEmpty()) { // Selecting the first result. formattedValue = results.get(0).getId(); statusCode = ElementExtractedValueStatus.VALID_VALUE; } } if (value instanceof Integer) { // Searching by ID. final int userId = (Integer) value; final User user = em().getReference(User.class, userId); if (user != null) { statusCode = ElementExtractedValueStatus.VALID_VALUE; formattedValue = userId; } } if (formattedValue == null) { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } break; default: formattedValue = stringValue; statusCode = ElementExtractedValueStatus.VALID_VALUE; break; } } break; case DEFAULT_CONTACT: if (stringValue.isEmpty()) { break; } switch (type.toDefaultContactFlexibleElementType()) { case COUNTRY: case DIRECT_MEMBERSHIP: case MAIN_ORG_UNIT: case ORGANIZATION_NAME: case SECONDARY_ORG_UNITS: case CREATION_DATE: // fall through case TOP_MEMBERSHIP: // Do not import it, it will be computed break; default: formattedValue = stringValue; statusCode = ElementExtractedValueStatus.VALID_VALUE; break; } break; case QUESTION: // Accepted formats: // Multiple : label(-label)+ // Single : label QuestionElement questionElement = (QuestionElement) flexibleElement; if (questionElement.getMultiple() != null && questionElement.getMultiple()) { String[] extractedQuestionValues = stringValue.split("-"); List<QuestionChoiceElement> choices = new ArrayList<QuestionChoiceElement>(); for (QuestionChoiceElement choice : questionElement.getChoices()) { final String choiceLabel; if (choice.getCategoryElement() != null) { choiceLabel = choice.getCategoryElement().getLabel(); } else { choiceLabel = choice.getLabel(); } for (String questionValue : extractedQuestionValues) { if (choiceLabel.trim().equals(questionValue.trim())) { choices.add(choice); } } } if (!choices.isEmpty()) { formattedValue = Collections.join(choices, new Collections.Mapper<QuestionChoiceElement, String>() { @Override public String forEntry(QuestionChoiceElement entry) { return entry.getId().toString(); } }, ValueResultUtils.DEFAULT_VALUE_SEPARATOR); statusCode = ElementExtractedValueStatus.VALID_VALUE; } else { statusCode = ElementExtractedValueStatus.INVALID_QUESTION_VALUE; } } else { for (QuestionChoiceElement choice : questionElement.getChoices()) { final String choiceLabel; if (choice.getCategoryElement() != null) { choiceLabel = choice.getCategoryElement().getLabel(); } else { choiceLabel = choice.getLabel(); } if (choiceLabel.equals(stringValue)) { formattedValue = choice.getId(); statusCode = ElementExtractedValueStatus.VALID_VALUE; break; } } } break; case TEXT_AREA: // Accepted formats: // Type DATE -> dd/MM/yyyy // Type NUMBER -> 0 // Type NUMBER + decimal -> 0.00 // Type PARAGRAPH -> * // Type TEXT -> * TextAreaElement textAreaElement = (TextAreaElement) flexibleElement; switch (type.toTextAreaType()) { case DATE: { if (value instanceof Date) { formattedValue = (Date) value; statusCode = ElementExtractedValueStatus.VALID_VALUE; } else if (value instanceof String) { SimpleDateFormat defaultFormat = new SimpleDateFormat("dd/MM/yyyy"); try { formattedValue = defaultFormat.parse(stringValue); statusCode = ElementExtractedValueStatus.VALID_VALUE; } catch (ParseException e) { statusCode = ElementExtractedValueStatus.INVALID_DATE_VALUE; } } if( ElementExtractedValueStatus.VALID_VALUE.equals(statusCode)){ Date dateValue = (Date) formattedValue; Date minValue = textAreaElement.getMinValue() != null ? new Date(textAreaElement.getMinValue()) : null; Date maxValue = textAreaElement.getMaxValue() != null ? new Date(textAreaElement.getMaxValue()) : null; boolean isValueCorrect = !((minValue != null && dateValue.before(minValue)) || (maxValue != null && dateValue.after(minValue))); if(!isValueCorrect) { statusCode = ElementExtractedValueStatus.FORBIDDEN_VALUE; } } } break; case NUMBER: { if (textAreaElement.getIsDecimal()) { if(value instanceof String) { try{ formattedValue = Double.valueOf(stringValue); statusCode = ElementExtractedValueStatus.VALID_VALUE; } catch(NumberFormatException nfe) { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } } else if (value instanceof Number) { formattedValue = ((Number)value).doubleValue(); statusCode = ElementExtractedValueStatus.VALID_VALUE; } else { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } if( ElementExtractedValueStatus.VALID_VALUE.equals(statusCode)){ Double numberValue = (Double) formattedValue; Long minValue = textAreaElement.getMinValue() != null ? textAreaElement.getMinValue() : null; Long maxValue = textAreaElement.getMaxValue() != null ? textAreaElement.getMaxValue() : null; boolean isValueCorrect = !((minValue != null && numberValue < minValue) || (maxValue != null && numberValue > maxValue)); if(!isValueCorrect) { statusCode = ElementExtractedValueStatus.FORBIDDEN_VALUE; } } } else { if(value instanceof String) { try{ formattedValue = Long.valueOf(stringValue); statusCode = ElementExtractedValueStatus.VALID_VALUE; } catch(NumberFormatException nfe) { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } } else if (value instanceof Number) { formattedValue = ((Number)value).longValue(); statusCode = ElementExtractedValueStatus.VALID_VALUE; } else { statusCode = ElementExtractedValueStatus.INVALID_NUMBER_VALUE; } if( ElementExtractedValueStatus.VALID_VALUE.equals(statusCode)){ Long numberValue = (Long) formattedValue; Long minValue = textAreaElement.getMinValue() != null ? textAreaElement.getMinValue() : null; Long maxValue = textAreaElement.getMaxValue() != null ? textAreaElement.getMaxValue() : null; boolean isValueCorrect = !((minValue != null && numberValue < minValue) || (maxValue != null && numberValue > maxValue)); if(!isValueCorrect) { statusCode = ElementExtractedValueStatus.FORBIDDEN_VALUE; } } } } break; case PARAGRAPH: case TEXT: { formattedValue = String.valueOf(value); statusCode = ElementExtractedValueStatus.VALID_VALUE; } default: break; } break; case TRIPLETS: // Accepted formats: // Code-Name-Date // IGNORED-Code-Name:Date String[] extractedTripletValues = stringValue.split("-"); if(extractedTripletValues.length == 3){ String[] namePeriod = extractedTripletValues[2].split(":"); if(namePeriod.length == 2) { String[] arrayTripletValues = new String[3]; arrayTripletValues[0] = extractedTripletValues[1]; arrayTripletValues[1] = namePeriod[0]; arrayTripletValues[2] = namePeriod[1]; formattedValue = arrayTripletValues; statusCode = ElementExtractedValueStatus.VALID_VALUE; } else { formattedValue = extractedTripletValues; statusCode = ElementExtractedValueStatus.VALID_VALUE; } } else { statusCode = ElementExtractedValueStatus.INVALID_TRIPLET_VALUE; } break; default: LOGGER.warn("Unsupported flexible element type: {}", type); break; } } valueStatus[0] = formattedValue; valueStatus[1] = statusCode; return valueStatus; } protected void logWarnFormatImportTypeIncoherence() { LOGGER.warn("Incoherence in ImporationScheme fileFormat (" + ImportationSchemeFileFormat.getStringValue(scheme.getFileFormat()) + " and its importType " + ImportationSchemeImportType.getStringValue(scheme.getImportType())); } /** * Return the boolean version of the checkbox element value * * @param valueResult * @param element * @return */ public Boolean getCheckboxValue(final ValueResult valueResult, final FlexibleElement element) { Boolean value = false; if (valueResult != null && valueResult.getValueObject() != null) { if (((String) valueResult.getValueObject()).equalsIgnoreCase("true")) value = true; } return value; } public HashSet<Integer> getContactListValue(ValueResult valueResult) { if (valueResult == null || valueResult.getValueObject() == null) { return new HashSet<>(0); } String[] values = valueResult.getValueObject().split(","); HashSet<Integer> contactIds = new HashSet<>(values.length); for (String value : values) { contactIds.add(Integer.parseInt(value)); } return contactIds; } protected int getColumnFromReference(String reference) { int column = 0; // First, try and see if the user used a letter to reference the column. Pattern MY_PATTERN = Pattern.compile("[a-zA-Z]+\\.?"); Matcher m = MY_PATTERN.matcher(reference); if (m.find()) { String letters = m.group(0); column = 0; for (int i = 0; i < letters.length(); i++) { column = column * 26 + (getNumericValuefromCharacter(letters.charAt(i)) + 1); } if(column > 0) { column -= 1; } } else { // Then see if the user used a number to reference the column. try { column = Integer.parseInt(reference); } catch(NumberFormatException e) { // Ignored. } } return column; } protected int getRowFromReference(String reference) { int row = 0; Pattern MY_PATTERN = Pattern.compile("[0-9]+\\.?"); Matcher m = MY_PATTERN.matcher(reference); if (m.find()) { String numbers = m.group(0); row = Integer.valueOf(numbers) - 1; } return row; } protected EntityManager em() { return injector.getProvider(EntityManager.class).get(); } // -- // Privates methods. // -- private VariableFlexibleElement getKeyOfCurrentSchemeModel() { try { return em().createQuery("SELECT vfe FROM VariableFlexibleElement AS vfe WHERE vfe.isKey = true AND vfe.importationSchemeModel = :schemeModel", VariableFlexibleElement.class) .setParameter("schemeModel", schemeModel) .getSingleResult(); } catch (NoResultException e) { LOGGER.trace("No key was found in the scheme model " + schemeModel, e); return null; } } private FlexibleElementDTO toDTO(FlexibleElement element) { final FlexibleElementDTO dto; final LogicalElementType type = element.type(); switch (type.toElementTypeEnum()) { case CHECKBOX: dto = new CheckboxElementDTO(); break; case CONTACT_LIST: dto = new ContactListElementDTO(); break; case COMPUTATION: dto = new ComputationElementDTO(); break; case CORE_VERSION: dto = new CoreVersionElementDTO(); break; case DEFAULT: if (type == DefaultFlexibleElementType.BUDGET) { dto = new BudgetElementDTO(); } else if (type == DefaultFlexibleElementType.BUDGET_RATIO) { dto = new BudgetRatioElementDTO(); } else { dto = new DefaultFlexibleElementDTO(type.toDefaultFlexibleElementType()); } break; case FILES_LIST: dto = new FilesListElementDTO(); break; case INDICATORS: dto = new IndicatorsListElementDTO(); break; case MESSAGE: dto = new MessageElementDTO(); break; case QUESTION: dto = new QuestionElementDTO(); break; case REPORT: dto = new ReportElementDTO(); break; case REPORT_LIST: dto = new ReportListElementDTO(); break; case TEXT_AREA: dto = new TextAreaElementDTO(type.toTextAreaType()); break; case TRIPLETS: dto = new TripletsListElementDTO(); break; default: throw new UnsupportedOperationException("Flexible element of type '" + type + "' is not supported."); } return mapper.map(element, dto); } }