package org.sigmah.server.servlet.exporter.models; /* * #%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.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.rmi.server.ExportException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import javax.persistence.EntityManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.sigmah.server.domain.PhaseModel; import org.sigmah.server.domain.ProjectModel; import org.sigmah.server.domain.ProjectModelVisibility; import org.sigmah.server.domain.User; import org.sigmah.server.domain.category.CategoryElement; import org.sigmah.server.domain.category.CategoryType; import org.sigmah.server.domain.element.BudgetElement; import org.sigmah.server.domain.element.BudgetSubField; 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.ReportElement; import org.sigmah.server.domain.element.ReportListElement; import org.sigmah.server.domain.layout.LayoutConstraint; import org.sigmah.server.domain.layout.LayoutGroup; import org.sigmah.server.domain.report.ProjectReportModel; import org.sigmah.shared.dto.referential.ProjectModelStatus; import org.sigmah.shared.dto.referential.ProjectModelType; /** * Exports and imports project models. * * @author Raphaël Calabro (rcalabro@ideia.fr) V1.3 * @author Mehdi Benabdeslam (mehdi.benabdeslam@netapsys.fr) V2.0 */ public class ProjectModelHandler implements ModelHandler { private final static Log LOG = LogFactory.getLog(ProjectModelHandler.class); private ProjectModelType projectModelType = ProjectModelType.NGO; @Override public void importModel(InputStream inputStream, EntityManager em, User user) throws Exception { ObjectInputStream objectInputStream; em.getTransaction().begin(); try { objectInputStream = new ObjectInputStream(inputStream); ProjectModel projectModel = (ProjectModel) objectInputStream.readObject(); // Sets the new model as a draft. projectModel.setStatus(ProjectModelStatus.DRAFT); final HashMap<Object, Object> modelesReset = new HashMap<Object, Object>(); final HashSet<Object> modelesImport = new HashSet<Object>(); projectModel.resetImport(); saveProjectFlexibleElement(projectModel, em, modelesReset, modelesImport, user); // Attaching the new model to the current user's organization final ProjectModelVisibility visibility = new ProjectModelVisibility(); visibility.setModel(projectModel); visibility.setType(projectModelType); visibility.setOrganization(user.getOrganization()); final ArrayList<ProjectModelVisibility> visibilities = new ArrayList<ProjectModelVisibility>(); visibilities.add(visibility); projectModel.setVisibilities(visibilities); // Set the status to DRAFT projectModel.setStatus(ProjectModelStatus.DRAFT); // TODO ?? for (PhaseModel pm : projectModel.getPhaseModels()) { for (LayoutGroup lg : pm.getLayout().getGroups()) { for (LayoutConstraint lc : lg.getConstraints()) { FlexibleElement fe = lc.getElement(); System.out.println(fe.getClass() + " id " + fe.getId()); if (fe instanceof QuestionElement) { for (QuestionChoiceElement qce : ((QuestionElement) fe).getChoices()) { System.out.println(qce.getClass() + " id " + qce.getId()); } } } } } em.merge(projectModel); em.getTransaction().commit(); LOG.debug("The project model '" + projectModel.getName() + "' has been imported successfully."); } catch (IOException | ClassNotFoundException e) { LOG.debug("Error while importing a project model.", e); throw new Exception("Error while importing a project model.", e); } } @Override public String exportModel(OutputStream outputStream, String identifier, EntityManager em) throws Exception { String name = ""; if (identifier == null) { throw new ExportException("The identifier is missing."); } final Integer projectModelId = Integer.parseInt(identifier); final ProjectModel hibernateModel = em.find(ProjectModel.class, projectModelId); if (hibernateModel == null) throw new ExportException("No project model is associated with the identifier '" + identifier + "'."); name = hibernateModel.getName(); // Removing superfluous links hibernateModel.setVisibilities(null); // Stripping hibernate proxies from the model. final ProjectModel realModel = Realizer.realize(hibernateModel); // Serialization try { final ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(realModel); } catch (Exception ex) { throw new Exception("An error occured while serializing the project model " + projectModelId, ex); } return name; } /** * Define the default project model type used when importing a project model. * * @param projectModelType */ public void setProjectModelType(ProjectModelType projectModelType) { this.projectModelType = projectModelType; } /** * Save the flexible elements of imported project model. * * @param projectModel * the imported project model * @param em * the entity manager */ private void saveProjectFlexibleElement(ProjectModel projectModel, EntityManager em, HashMap<Object, Object> modelesReset, HashSet<Object> modelesImport, User user) { // ProjectModel → Banner → Layout → Groups → Constraints if (projectModel.getProjectBanner() != null && projectModel.getProjectBanner().getLayout() != null) { final List<LayoutGroup> bannerLayoutGroups = projectModel.getProjectBanner().getLayout().getGroups(); saveLayoutGroups(bannerLayoutGroups, em, modelesReset, modelesImport, user); } // ProjectModel → Detail → Layout → Groups → Constraints if (projectModel.getProjectDetails() != null && projectModel.getProjectDetails().getLayout() != null) { final List<LayoutGroup> detailLayoutGroups = projectModel.getProjectDetails().getLayout().getGroups(); saveLayoutGroups(detailLayoutGroups, em, modelesReset, modelesImport, user); } // ProjectModel → Phases → Layout → Groups → Constraints List<PhaseModel> phases = projectModel.getPhaseModels(); if (phases != null) { projectModel.setPhaseModels(null); em.persist(projectModel); for (PhaseModel phase : phases) { phase.setParentProjectModel(projectModel); if (phase.getLayout() != null) { final List<LayoutGroup> phaseLayoutGroups = phase.getLayout().getGroups(); saveLayoutGroups(phaseLayoutGroups, em, modelesReset, modelesImport, user); } if (phase.getDefinition() != null) { em.persist(phase.getDefinition()); } em.persist(phase); } projectModel.setPhaseModels(phases); } } private void saveLayoutGroups(final List<LayoutGroup> layoutGroups, EntityManager em, HashMap<Object, Object> modelesReset, HashSet<Object> modelesImport, User user) { final HashSet<Integer> reportModelsId = new HashSet<Integer>(); if (layoutGroups != null) { for (LayoutGroup layoutGroup : layoutGroups) { final List<LayoutConstraint> layoutConstraints; if (layoutGroup != null) layoutConstraints = layoutGroup.getConstraints(); else layoutConstraints = null; if (layoutConstraints != null) { // Iterating over the constraints for (LayoutConstraint layoutConstraint : layoutConstraints) { if (layoutConstraint != null && layoutConstraint.getElement() != null) { final FlexibleElement element = layoutConstraint.getElement(); // Do not persist an element twice. if (em.contains(element)) { continue; } // Initialize export flags of flexible element element.initializeExportFlags(); // If the current element is a QuestionElement if (element instanceof QuestionElement) { final QuestionElement questionElement = (QuestionElement) element; List<QuestionChoiceElement> questionChoiceElements = questionElement.getChoices(); CategoryType type = questionElement.getCategoryType(); if (questionChoiceElements != null || type != null) { questionElement.setChoices(null); questionElement.setCategoryType(null); em.persist(questionElement); // Save QuestionChoiceElement with their // QuestionElement parent(saved above) if (questionChoiceElements != null) { for (QuestionChoiceElement questionChoiceElement : questionChoiceElements) { if (questionChoiceElement != null) { questionChoiceElement.setId(null); questionChoiceElement.setParentQuestion(questionElement); CategoryElement categoryElement = questionChoiceElement.getCategoryElement(); if (categoryElement != null) { questionChoiceElement.setCategoryElement(null); em.persist(questionChoiceElement); saveProjectModelCategoryElement(categoryElement, em, modelesReset, modelesImport); questionChoiceElement.setCategoryElement(categoryElement); em.merge(questionChoiceElement); } else { em.persist(questionChoiceElement); } } } // Set saved QuestionChoiceElement to // QuestionElement parent and update it questionElement.setChoices(questionChoiceElements); } // Save the Category type of QuestionElement // parent(saved above) if (type != null) { if (em.find(CategoryType.class, type.getId()) == null) { List<CategoryElement> typeElements = type.getElements(); if (typeElements != null) { type.setElements(null); em.merge(type); for (CategoryElement categoryElement : typeElements) { if (em.find(CategoryElement.class, categoryElement.getId()) == null) { categoryElement.setParentType(type); saveProjectModelCategoryElement(categoryElement, em, modelesReset, modelesImport); } } type.setElements(typeElements); em.merge(type); } } // Set the saved CategoryType to // QuestionElement parent and update it questionElement.setCategoryType(type); } // Update the QuestionElement parent em.merge(questionElement); } else { em.persist(element); } } // Report element else if (element instanceof ReportElement) { final ReportElement reportElement = (ReportElement) element; final ProjectReportModel oldModel = reportElement.getModel(); if (oldModel != null) { final int oldModelId = oldModel.getId(); final ProjectReportModel newModel; if (!reportModelsId.contains(oldModelId)) { oldModel.resetImport(new HashMap<Object, Object>(), new HashSet<Object>()); oldModel.setOrganization(user.getOrganization()); newModel = oldModel; ProjectReportModelHandler.saveProjectReportModelElement(newModel, em); em.persist(newModel); reportElement.setModel(newModel); em.persist(reportElement); reportModelsId.add(reportElement.getModel().getId()); } // If the report model has been already // saved, it is re-used. else { newModel = em.find(ProjectReportModel.class, oldModelId); reportElement.setModel(newModel); em.persist(reportElement); } } } // Reports list element else if (element instanceof ReportListElement) { final ReportListElement reportListElement = (ReportListElement) element; final ProjectReportModel oldModel = reportListElement.getModel(); if (oldModel != null) { final int oldModelId = oldModel.getId(); final ProjectReportModel newModel; if (!reportModelsId.contains(oldModelId)) { oldModel.resetImport(new HashMap<Object, Object>(), new HashSet<Object>()); oldModel.setOrganization(user.getOrganization()); newModel = oldModel; ProjectReportModelHandler.saveProjectReportModelElement(newModel, em); em.persist(newModel); reportListElement.setModel(newModel); em.persist(reportListElement); reportModelsId.add(reportListElement.getModel().getId()); } // If the report model has been already // saved, it is re-used. else { newModel = em.find(ProjectReportModel.class, oldModelId); reportListElement.setModel(newModel); em.persist(reportListElement); } } } // Budget element else if(element instanceof BudgetElement) { final BudgetElement budgetElement = (BudgetElement) element; final List<BudgetSubField> subFields = budgetElement.getBudgetSubFields(); final BudgetSubField ratioDividend = budgetElement.getRatioDividend(); final BudgetSubField ratioDivisor = budgetElement.getRatioDivisor(); budgetElement.setBudgetSubFields(null); budgetElement.setRatioDividend(null); budgetElement.setRatioDivisor(null); em.persist(budgetElement); final ArrayList<BudgetSubField> allSubFields = new ArrayList<BudgetSubField>(subFields); allSubFields.add(ratioDividend); allSubFields.add(ratioDivisor); for(final BudgetSubField subField : allSubFields) { subField.setBudgetElement(budgetElement); em.persist(budgetElement); } budgetElement.setBudgetSubFields(subFields); budgetElement.setRatioDividend(ratioDividend); budgetElement.setRatioDivisor(ratioDivisor); em.merge(budgetElement); } // Others elements else { em.persist(layoutConstraint.getElement()); } } } } } } } /** * Save the category element of a question choice element. * * @param categoryElement * the category element to save. * @param em * the entity manager. */ private void saveProjectModelCategoryElement(CategoryElement categoryElement, EntityManager em, HashMap<Object, Object> modelesReset, HashSet<Object> modelesImport) { if (!modelesImport.contains(categoryElement)) { modelesImport.add(categoryElement); if (!modelesReset.containsKey(categoryElement)) { CategoryElement key = categoryElement; modelesReset.put(key, categoryElement); categoryElement.setId(null); CategoryType parentType = categoryElement.getParentType(); if (!modelesImport.contains(parentType)) { modelesImport.add(parentType); if (!modelesReset.containsKey(parentType)) { CategoryType parentKey = parentType; modelesReset.put(parentKey, parentType); parentType.setId(null); List<CategoryElement> elements = parentType.getElements(); if (elements != null) { parentType.setElements(null); em.persist(parentType); for (CategoryElement element : elements) { categoryElement.setParentType(parentType); saveProjectModelCategoryElement(element, em, modelesReset, modelesImport); } parentType.setElements(elements); em.merge(parentType); } else { em.persist(parentType); } } else { parentType = (CategoryType) modelesReset.get(parentType); } } categoryElement.setParentType(parentType); em.persist(categoryElement); } else { categoryElement = (CategoryElement) modelesReset.get(categoryElement); } } } }