Java tutorial
/*L * Copyright HealthCare IT, Inc. * * Distributed under the OSI-approved BSD 3-Clause License. * See http://ncip.github.com/edct-formbuilder/LICENSE.txt for details. */ package com.healthcit.cacure.businessdelegates; import gov.nih.nci.cadsr.domain.DataElement; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import com.healthcit.cacure.businessdelegates.beans.SkipAffecteesBean; import com.healthcit.cacure.cadsr.CADSRManager; import com.healthcit.cacure.dao.AnswerDao; import com.healthcit.cacure.dao.ContentElementDao; import com.healthcit.cacure.dao.ExternalQuestionElementDao; import com.healthcit.cacure.dao.FormElementDao; import com.healthcit.cacure.dao.LinkElementDao; import com.healthcit.cacure.dao.QuestionDao; import com.healthcit.cacure.dao.QuestionElementDao; import com.healthcit.cacure.dao.QuestionTableDao; import com.healthcit.cacure.dao.SkipPatternDao; import com.healthcit.cacure.dao.TableElementDao; import com.healthcit.cacure.enums.ItemOrderingAction; import com.healthcit.cacure.model.Answer; import com.healthcit.cacure.model.Answer.AnswerType; import com.healthcit.cacure.model.AnswerSkipRule; import com.healthcit.cacure.model.AnswerValue; import com.healthcit.cacure.model.BaseForm; import com.healthcit.cacure.model.BaseQuestion; import com.healthcit.cacure.model.BaseSkipPatternDetail; import com.healthcit.cacure.model.Category; import com.healthcit.cacure.model.ContentElement; import com.healthcit.cacure.model.Description; import com.healthcit.cacure.model.ExternalQuestion; import com.healthcit.cacure.model.ExternalQuestionElement; import com.healthcit.cacure.model.FormElement; import com.healthcit.cacure.model.FormElementSkipRule; import com.healthcit.cacure.model.LinkElement; import com.healthcit.cacure.model.Question; import com.healthcit.cacure.model.QuestionElement; import com.healthcit.cacure.model.QuestionSkipRule; import com.healthcit.cacure.model.TableColumn; import com.healthcit.cacure.model.TableElement; import com.healthcit.cacure.model.TableQuestion; import com.healthcit.cacure.security.UnauthorizedException; import com.healthcit.cacure.web.FormElementSearchCriteria; public class QuestionAnswerManager { private static final Logger logger = Logger.getLogger(QuestionAnswerManager.class); private static String SPLITTER = ","; @Autowired QuestionDao qstDao; @Autowired QuestionTableDao tqDao; @Autowired AnswerDao answerDao; @Autowired SkipPatternDao skipDao; @Autowired FormManager formManager; @Autowired QuestionElementDao qeDao; @Autowired TableElementDao teDao; @Autowired ExternalQuestionElementDao eqeDao; @Autowired ContentElementDao cDao; @Autowired LinkElementDao linkDao; @Autowired FormElementDao formElementDao; @Autowired CADSRManager cadsrManager; @Transactional public FormElement updateFormElement(FormElement fe) { if (!fe.getForm().isEditable()) { throw new UnauthorizedException( "A locked form and its entities can be edited only by the user who has locked the form"); } //Commented since we don't have possibility to edit such question from UI /*if (fe.getForm().isLibraryForm() && isLinked(fe)) { throw new UnauthorizedException("This FormElement belongs to the library and cannot be modified"); }*/ //prepare question entity for being updated fe.prepareForUpdate(); // update all links associated with this FormElement, as appropriate // (NOTE: This method must be invoked BEFORE the actual updates to the FormElement are executed, // since we need to be able to access the pre-update values) updateAssociatedLinks(fe); FormElement mergedElement = null; /* If Links are edited then we need to break the link and do a deep copy of the parent element * with newly generated UUIDs all the way down */ if (fe instanceof LinkElement) { //create new FormElement //remove linkElement LinkElement link = (LinkElement) fe; String sourceUuid = link.getSourceId(); FormElement source = linkDao.getLinkSource(sourceUuid); FormElement clone = source.clone(); mergedElement = createFormElement(clone); linkDao.delete(link); } else { formElementDao.update(fe); if (fe instanceof QuestionElement) { QuestionElement questionElement = (QuestionElement) fe; this.answerDao.removeNotActualQuestionAnswers(questionElement.getQuestion().getId(), questionElement.getQuestion().getAnswer().getId()); } mergedElement = fe; } skipDao.skipPatternCleanup(); // processLinkedFormElements(fe); return mergedElement; } private boolean isLinked(FormElement fe) { /*List<LinkElement> links = linkDao.getLinkedFormElements(fe); boolean isLinked = false; if(links != null && links.size()>0) { isLinked = true; }*/ boolean isLinked = false; int linkCount = fe.getLinkCount(); if (linkCount > 0) { isLinked = true; } return isLinked; // return this.linkDao.hasLinkedFormElements(fe); } public FormElement createFormElement(FormElement fe) { FormElement mergedElement = null; formElementDao.create(fe); return mergedElement; } @Transactional public void deleteFormElementByID(Long id) { FormElement fe = formElementDao.getById(id); if (fe.getForm().getModule().isLibrary() && isLinked(fe)) { throw new UnauthorizedException("This FormElement belongs to the library and cannot be modified"); } deleteFormElement(fe); } private void deleteFormElement(FormElement e) { //prepare question entity for being deleted if (!e.getForm().isEditable()) { throw new UnauthorizedException( "A locked form and its entities can be modified only by the user who has locked the form"); } if (e.getForm().getModule().isLibrary() && isLinked(e)) { throw new UnauthorizedException("This FormElement belongs to the library and cannot be modified"); } /* Check if Element has links if there are no links pointing t it, delete it */ if (hasLinks(e)) { throw new UnauthorizedException("the Form Element has links and cannot be deleted."); } e.prepareForDelete(); // feDao.deleteLinks(e); formElementDao.delete(e); skipDao.skipPatternCleanup(); } public boolean hasLinks(FormElement element) { boolean hasLinks = false; List<LinkElement> links = linkDao.getLinkedFormElements(element); if (links != null && links.size() > 0) { hasLinks = true; } return hasLinks; } public boolean isSkip(Long questionId) { if (skipDao.isSkip(questionId)) { return true; } return false; } public Set<String> getSkipsUuidsFrom(Set<String> uuids) { return skipDao.getSkipsUuidsFrom(uuids); } public boolean isAnswerValueSkip(String permAnswerValueId, Long formId) { if (skipDao.isAnswerValueSkip(permAnswerValueId, formId)) { return true; } return false; } public boolean isAnswerValueSkipTableRow(Long answerId) { if (skipDao.isAnswerValueSkipTableRow(answerId)) { return true; } return false; } public Map<String, String> getQuestionIdbyAnswerValueId(String answerValueId) { return skipDao.getQuestionIdbyAnswerValueId(answerValueId); } @Deprecated public void deleteAnswerValueSkip(String permAnswerValueId) { skipDao.deleteAnswerValueSkip(permAnswerValueId); } public List<BaseQuestion> getAllFormQuestions(Long formId) { return qstDao.getAllFormQuestions(formId); } /* public Question getQuestion(Long id) { return qstDao.getById(id); } */ public FormElement getFormElement(Long id) { FormElement formElement = formElementDao.getById(id); return formElement; } public FormElement getFormElement(String uuid) { FormElement formElement = formElementDao.getByUUID(uuid); return formElement; } public ContentElement getContentElement(Long id) { return cDao.getById(id); } public QuestionElement getQuestionElement(Long id) { return qeDao.getById(id); } public TableElement getTableElement(Long id) { return teDao.getById(id); } public ExternalQuestionElement getExternalQuestionElement(Long id) { return eqeDao.getById(id); } /** * changes the order in list between two consecutive items. * <b>questionId</b> is id of target item. * @param questionId Long * @param ordType ItemOrderingAction */ public void moveFormElementInForm(Long elementId, ItemOrderingAction ordType) { //get pair of items to be changed between themselves List<FormElement> list = formElementDao.getAdjacentPairOfFormElements(elementId, ordType); // if single form returned - no need to move if (list.size() == 2) { FormElement element0 = list.get(0); //target item FormElement element1 = list.get(1); //item to be replaced with if (!element0.getForm().isEditable()) { throw new UnauthorizedException( "A locked form and its entities can be modified only by the user who has locked the form"); } //change the order between items Integer ord0 = element0.getOrd(); Integer ord1 = element1.getOrd(); // first must use invalid order to work around unique constraint // it works because save method has it's own transaction // it's important to update question1, not question0! element1.setOrd(-1); saveFormElement(element1); // modify ord to an actual value element0.setOrd(ord1); element1.setOrd(ord0); //persist changes - the order is important here! saveFormElement(element0); saveFormElement(element1); } } @Transactional public void saveFormElement(FormElement fe) { formElementDao.save(fe); } /** * @param formId Long * @return List<Question> ordered by ord that fetches list of answers */ public List<FormElement> getAllFormElements(Long formId) { return qeDao.getAllFormElements(formId); } public List<FormElement> getFormElementsByTextWithinCategories(long formId, String q, long... categoryIds) { if (StringUtils.isBlank(q) && (categoryIds == null || categoryIds.length == 0)) { return qeDao.getAllFormElements(formId); } return qeDao.getFormElementsByTextWithinCategories(formId, q, categoryIds); } public List<FormElement> searchFormElements(int searchCriteria, String searchText, Long categoryId) { FormElementSearchCriteria criteria = new FormElementSearchCriteria(searchCriteria, searchText, categoryId); return getFormElementBySearchCriteria(criteria); } public List<FormElement> getFormElementsByUuid(Set<String> uuids) { return qeDao.getFormElementsByUuid(uuids); } /** * @param criteria String * @return List<Question> */ public List<FormElement> getFormElementBySearchCriteria(FormElementSearchCriteria criteria) { List<FormElement> list = null; switch (criteria.getSearchType()) { case FormElementSearchCriteria.SEARCH_BY_TEXT: list = qeDao.getFormElementsByText(criteria.getSearchText()); break; case FormElementSearchCriteria.SEARCH_BY_CATEGORY: list = qeDao.getQuestionLibraryFormElementsByCategory(criteria.getCategoryId()); break; case FormElementSearchCriteria.SEARCH_BY_TEXT_WITHIN_CATEGORY: list = qeDao.getQuestionLibraryFormElementsByTextWithinCategories(criteria.getSearchText(), criteria.getCategoryId()); break; case FormElementSearchCriteria.SEARCH_BY_CADSR_TEXT: logger.debug("CADSR Search by text..."); list = showCADSRFormElementSearchResults(criteria); break; case FormElementSearchCriteria.SEARCH_BY_CADSR_CART_USER: logger.debug("CADSR Search by Cart User..."); list = showCADSRFormElementSearchResults(criteria); break; } return list; } /** * @param questionId Long * @return Question */ public BaseQuestion getQuestionFetchesChildren(Long questionId) { return qstDao.getQuestionFetchesChildren(questionId); } public FormElement getFormElementFetchesChildrenByUuid(String uuid) { return qeDao.getFormElementFetchesChildrenByUuid(uuid); } /** * @param formId Long * @param uuid String * @return true if question exists in form */ public boolean isQuestionAlreadyExistsInForm(Long formId, String uuid) { return qstDao.isQuestionAlreadyExistsInForm(formId, uuid); } /** * @param formId Long * @param questionId Long * @return true if question exists in form * NOTE: Currently using the question's UUID to identify whether the question exists in the form, * instead of the primary key. * See: isQuestionAlreadyExistsInForm(Long formId, String uuid). */ @Deprecated public boolean isQuestionAlreadyExistsInForm(Long formId, Long questionId) { return qstDao.isQuestionAlreadyExistsInForm(formId, questionId); } /** * Adding a Question entity. * @param q Question * @param formId Long * @return Question */ @Transactional public Question addNewQestion(Question q, Long qElementId) { QuestionElement qElement = qeDao.getById(qElementId); q.setQuestionElement(qElement); //prepare question entity for being persisted q.prepareForPersist(); qstDao.create(q); return q; } @Transactional public TableQuestion addNewTableQestion(TableQuestion q, Long qElementId) { TableElement qElement = teDao.getById(qElementId); q.setTable(qElement); //prepare question entity for being persisted q.prepareForPersist(); tqDao.create(q); return q; } private FormElement _addNewFormElement(FormElement fe, Long formId) { //This is used when linkElement is editied, the newly created formElement should inherit the // order from the LinkElement rather than creating the new one. Integer ord = fe.getOrd(); if (ord == null) { ord = qeDao.calculateNextOrdNumber(formId); if (ord == null) { ord = 1; } } BaseForm form = formManager.getForm(formId); form.addElement(fe); //prepare question entity for being persisted fe.prepareForPersist(); //calculate and set Ord Number for question fe.setOrd(ord); if (fe instanceof QuestionElement) { qeDao.create((QuestionElement) fe); } else if (fe instanceof ContentElement) { cDao.create((ContentElement) fe); } else if (fe instanceof TableElement) { teDao.create((TableElement) fe); } else if (fe instanceof ExternalQuestionElement) { eqeDao.create((ExternalQuestionElement) fe); } else if (fe instanceof LinkElement) { linkDao.create((LinkElement) fe); } return fe; } @Transactional public FormElement addNewFormElement(FormElement fe, Long formId) { //taken out into a separate method in order to be able to call it as part of other transaction fe = _addNewFormElement(fe, formId); return fe; } @Transactional public void importFormElements(Long formId, String[][] elementSet, int searchCriteria) { logger.debug("In import FormElements method"); if (elementSet == null) elementSet = new String[][] {}; List<FormElement> newElements = buildNewQuestions(elementSet, searchCriteria); for (int i = 0; i < newElements.size(); ++i) { FormElement newElement = newElements.get(i); @SuppressWarnings("unused") FormElement persistedQuestion = addNewFormElement(newElement, formId); } } @Transactional public List<FormElement> buildNewQuestions(String[][] questionSet, int searchCriteria) { List<FormElement> newElements = new ArrayList<FormElement>(); Map<String, DataElement> dataElements = new HashMap<String, DataElement>(); int numElements = questionSet.length; String[] questionIdList = new String[numElements]; String[] answerTypeList = new String[numElements]; String[] deletedAnswerValuesList = new String[numElements]; for (int i = 0; i < numElements; ++i) { questionIdList[i] = questionSet[i][0]; answerTypeList[i] = questionSet[i][1]; if (questionSet[i].length > 2) deletedAnswerValuesList[i] = questionSet[i][2]; } for (int i = 0; i < numElements; ++i) { String uuid = questionIdList[i]; // The following 2 variables are not used. I am leaving them in case the accessors are used // to load lazy collections - LK String answerType = answerTypeList[i]; HashSet<String> deletedAnswerValues = new HashSet<String>(); if (StringUtils.isNotBlank(deletedAnswerValuesList[i])) { deletedAnswerValues.addAll(Arrays.asList(deletedAnswerValuesList[i].split("\\s*,\\s*"))); } if (searchCriteria == FormElementSearchCriteria.SEARCH_BY_CADSR_TEXT // CADSR Text Search || searchCriteria == FormElementSearchCriteria.SEARCH_BY_CADSR_CART_USER) // CADSR Cart User Search { if (i == 0) dataElements = cadsrManager.findCADSRQuestionsById(StringUtils.join(questionIdList, SPLITTER)); DataElement dataElement = dataElements.get(uuid); if (dataElement != null) { FormElement newElement; AnswerType answerTypeEnumEntry = AnswerType.valueOf(answerType); newElement = cadsrManager.transformCADSRQuestion(dataElement, answerTypeEnumEntry, deletedAnswerValues); newElements.add(newElement); } } else // local { LinkElement linkElement = new LinkElement(); FormElement source = linkDao.getLinkSource(uuid); linkElement.setLearnMore(source.getLearnMore()); linkElement.setVisible(source.isVisible()); linkElement.setRequired(source.isRequired()); linkElement.setReadonly(source.isReadonly()); linkElement.setDescription(source.getDescription()); linkElement.setSource(source); newElements.add(linkElement); } } modifyShortNames(newElements); return newElements; } /** * This method returns a list of (non-persisted) FormBuilder Question entities * that correspond to the CADSR Question elements that match the search criteria * provided in the given string. * @author Oawofolu */ public List<FormElement> showCADSRFormElementSearchResults(FormElementSearchCriteria searchCriteria) { List<?> originalList = CADSRManager.getSearchResults(searchCriteria.getSearchText(), searchCriteria.getSearchType()); List<FormElement> transformedList = new ArrayList<FormElement>(); for (Object obj : originalList) { gov.nih.nci.cadsr.domain.DataElement question = (gov.nih.nci.cadsr.domain.DataElement) obj; ExternalQuestionElement transformedQuestion = cadsrManager.transformCADSRQuestion(question); transformedList.add(transformedQuestion); } return transformedList; } public List<Long> getLinkedFormElementIds(String linkId) { return linkDao.getLinkedFormElementIds(linkId); } public List<String> getLinkedFormElementDescriptions(String linkId) { if (NumberUtils.isNumber(linkId)) { FormElement formElement = formElementDao.getById(new Long(linkId)); String uuid = formElement.isLink() ? ((LinkElement) formElement).getSourceId() : formElement.getUuid(); return linkDao.getLinkedFormElementDescriptions(uuid); } else { return new ArrayList<String>(); } } public Set<String> getLinkedFormElementUuids(Set<String> linkUuids) { return linkDao.getLinkedFormElementUuids(linkUuids); } public List<Long> getLinkedSkippedFormElementIds(String linkId) { return linkDao.getLinkedSkippedFormElementIds(linkId); } public List<Long> getLinkedReadOnlyFormElementIds(String linkId) { return linkDao.getLinkedReadOnlyFormElementIds(linkId); } public void reorderTableQuestions(Long sourceQuestionId, Long targetQuestionId, boolean before) { TableQuestion question = getTableQuestion(targetQuestionId); if (!formManager.isEditableInCurrentContext(question.getParent().getForm())) { // The UI should never get the user here throw new UnauthorizedException("The QuestionnaireForm is not editable in the current context"); } tqDao.reorderQuestions(sourceQuestionId, targetQuestionId, before); } public void reorderFormElements(Long sourceElementId, Long targetElementId, boolean before) { FormElement formElement = this.formElementDao.getById(targetElementId); if (!formManager.isEditableInCurrentContext(formElement.getForm())) { // The UI should never get the user here throw new UnauthorizedException("The QuestionnaireForm is not editable in the current context"); } formElementDao.reorderFormElements(sourceElementId, targetElementId, before); } public TableQuestion getTableQuestion(Long questionId) { return tqDao.getById(questionId); } @Transactional public void deleteLink(Long id) { linkDao.delete(id); } public SkipAffecteesBean getAllPossibleSkipAffectees(final BaseForm form) { SkipAffecteesBean affecteesBean = new SkipAffecteesBean(); getAllPossibleSkipAffectees(affecteesBean, form); return affecteesBean; } public SkipAffecteesBean getAllPossibleSkipAffectees(final FormElement element) { SkipAffecteesBean affecteesBean = new SkipAffecteesBean(); getAllPossibleSkipAffectees(affecteesBean, element); return affecteesBean; } protected void getAllPossibleSkipAffectees(final SkipAffecteesBean affecteesBean, final BaseForm form) { affecteesBean.add(form); List<FormElement> elements = form.getElements(); for (FormElement element : elements) { getAllPossibleSkipAffectees(affecteesBean, element); } } protected void getAllPossibleSkipAffectees(final SkipAffecteesBean affecteesBean, final FormElement element) { affecteesBean.add(element); if (element.getQuestions() == null) { return; } for (BaseQuestion question : element.getQuestions()) { for (BaseSkipPatternDetail affectee : question.getSkipAffectees()) { Long affectedElementId = affectee.getFormElementId(); Long affectedFormId = affectee.getFormId(); if (affectedElementId != null) { FormElement affectedElement = formElementDao.getById(affectedElementId); // Since we are not currently checking for circular dependencies in skips, // we must make sure we are only adding this question // if it has not already been added to the master list; // otherwise we will have an infinite loop boolean hasCircularDependency = affecteesBean.getFormElements().contains(affectedElement); if (!hasCircularDependency) { getAllPossibleSkipAffectees(affecteesBean, affectedElement); } } else if (affectedFormId != null) { BaseForm affectedForm = formManager.getForm(affectedFormId); boolean hasCircularDependency = affecteesBean.getForms().contains(affectedForm); if (!hasCircularDependency) { getAllPossibleSkipAffectees(affecteesBean, affectedForm); } } } } } @Transactional public void unlink(FormElement fe, Long linkId, Long formId) { LinkElement link = (LinkElement) formElementDao.getById(linkId); FormElement source = link.getSourceElement(); updateSkips(fe, formId, source); Set<Category> categories = new LinkedHashSet<Category>(source.getCategories()); fe.setCategories(categories); // The new formelement will have its own separate list of descriptions Set<Description> descriptionList = new LinkedHashSet<Description>(fe.getDescriptionList()); for (Description description : descriptionList) description.setId(null); if (fe instanceof TableElement) { TableElement tableElement = (TableElement) fe; tableElement.setTableColumns(new ArrayList<TableColumn>()); TableQuestion identifyingQuestion = tableElement.getIdentifyingQuestion(); if (identifyingQuestion != null) { identifyingQuestion.setShortName("identifyingRowShortName-" + UUID.randomUUID().toString()); } } fe.resetId(); _addNewFormElement(fe, formId); deleteLink(linkId); // update the new FormElement with the new description list updateDescriptionList(fe, descriptionList); skipDao.skipPatternCleanup(); } public void updateSkips(FormElement newFormElement, Long newFormElementFormId, FormElement copiedFromFormElement) { Map<String, String> uuidMap = regenerateAnswerValuesPermanentIds(newFormElement); updateAnswerValuesPermanentIds(newFormElementFormId, copiedFromFormElement, uuidMap); } public void updateAnswerValuesPermanentIds(Long newFormElementFormId, FormElement formElement, Map<String, String> uuidMap) { List<? extends BaseQuestion> sourceQuestions = formElement.getQuestions(); /* Update skips with new permanentId of the answer and new formId */ for (BaseQuestion question : sourceQuestions) { Set<BaseSkipPatternDetail> skipAffectees = question.getSkipAffectees(); for (BaseSkipPatternDetail detail : skipAffectees) { //BaseSkipPattern skip = detail.getSkip(); if (detail.getFormElementId() != null) { FormElement skipOwner = formElementDao.getById(detail.getFormElementId()); if (skipOwner.getForm().getId().equals(newFormElementFormId)) { QuestionSkipRule skip = detail.getSkip(); if (skip.getIdentifyingAnswerValueUuId() != null) { String newUuid = uuidMap.get(skip.getIdentifyingAnswerValueUuId()); skip.setIdentifyingAnswerValueUuId(newUuid); } List<AnswerSkipRule> parts = skip.getSkipParts(); for (AnswerSkipRule part : parts) { String newUuid = uuidMap.get(part.getAnswerValueId()); if (newUuid != null && part.getFormId().equals(newFormElementFormId)) { part.setAnswerValueId(newUuid); } } } } } } } public Map<String, String> regenerateAnswerValuesPermanentIds(FormElement newFormElement) { Map<String, String> uuidMap = new HashMap<String, String>(); List<? extends BaseQuestion> questions = newFormElement.getQuestions(); if (questions != null) { for (BaseQuestion question : questions) { question.setId(null); question.setUuid(UUID.randomUUID().toString()); question.setSkipAffectees(new LinkedHashSet<BaseSkipPatternDetail>()); List<AnswerValue> answerValues = question.getAnswer().getAnswerValues(); //replace permanentId to the new one for (AnswerValue answerValue : answerValues) { if (StringUtils.isNotBlank(answerValue.getPermanentId())) { String newUuid = UUID.randomUUID().toString(); uuidMap.put(answerValue.getPermanentId(), newUuid); answerValue.setPermanentId(newUuid); } } } } return uuidMap; } @Transactional public void updateLink(FormElement fe) { LinkElement linkElement = (LinkElement) getFormElement(fe.getId()); prepareLinkSourceForUpdateLink(linkElement.getSourceElement(), fe); linkElement.setLearnMore(fe.getLearnMore()); linkElement.setRequired(fe.isRequired()); linkElement.setVisible(fe.isVisible()); linkElement.setReadonly(fe.isReadonly()); linkElement.setDescription(fe.getDescription()); if (linkElement.getSkipRule() != null) skipDao.delete(linkElement.getSkipRule()); linkElement.setSkipRule(fe.getSkipRule()); linkDao.update(linkElement); skipDao.skipPatternCleanup(); } @Transactional public void updateSourceCategories(long linkId, Set<Category> categories) { LinkElement linkElement = (LinkElement) getFormElement(linkId); FormElement sourceElement = linkElement.getSourceElement(); sourceElement.setCategories(categories); formElementDao.save(sourceElement); } /** * Makes any necessary updates to a LinkElement's source element before making updates to the link. * * (NOTE: These updates to the link source * are NOT to be applied when a LinkElement is being unlinked, * because the source element will be detached from the link.) * @param sourceElement * @param targetElement */ @Transactional public void prepareLinkSourceForUpdateLink(FormElement sourceElement, FormElement targetElement) { //update the list of descriptions in the source element before updating the link updateDescriptionList(sourceElement, targetElement.getDescriptionList()); // Whenever a LinkElement is being updated, // updates to the "main" description from the "descriptionList" collection // need to be manually propagated back to the source resetDescriptionInLinkSource(sourceElement, targetElement); } /** * When necessary, updates to the "main" description from the "descriptionList" collection are manually propagated back to the source * @param sourceElement * @param description */ @Transactional public void resetDescriptionInLinkSource(FormElement linkSourceElement, FormElement targetElement) { if (wasMainDescriptionChangedInLink(linkSourceElement, targetElement.getDescriptionList())) { linkSourceElement.setDescription(targetElement.getDescription()); } } public boolean wasMainDescriptionChangedInLink(FormElement linkSource, Set<Description> descriptionList) { boolean changed = true; for (Description description : descriptionList) { if (StringUtils.equals(linkSource.getDescription(), description.getDescription())) { return (changed = false); } } return changed; } /** * updates the list of descriptions in the source element before updating the link * @param linkSourceElement * @param descriptionList */ @Transactional public void updateDescriptionList(FormElement linkSourceElement, Set<Description> descriptionList) { Set<Description> oldDescriptionList = linkSourceElement.getDescriptionList(); Set<Description> newDescriptionList = new LinkedHashSet<Description>(); for (Description description : descriptionList) { if (description.isNew()) // if the description had never been previously persisted, perform an insert { newDescriptionList.add(description); } else // else perform an update { for (Description originalDescription : oldDescriptionList) { if (description.getId().equals(originalDescription.getId())) { originalDescription.setDescription(description.getDescription()); newDescriptionList.add(originalDescription); } } } } // persist the decription list changes to the DB linkSourceElement.setDescriptionList(newDescriptionList); formElementDao.save(linkSourceElement); } @Transactional public void updateAssociatedLinks(FormElement fe) { // 1. Update the description of all link elements associated with this FormElement, as appropriate // (When the description list is modified, any modified descriptions should also be propagated to the link elements as appropriate) formElementDao.updateAllFormElementsWithDescriptionChanged(fe); //2. .....ANY OTHER UPDATES...... } public void skipsDeepCopy(FormElement from, FormElement to) { FormElementSkipRule feSkipRule = from.getSkipRule(); if (feSkipRule == null) return; FormElementSkipRule _feSkipRule = feSkipRule.clone(); List<QuestionSkipRule> qSkipRules = feSkipRule.getQuestionSkipRules(); if (qSkipRules == null || qSkipRules.isEmpty()) return; for (QuestionSkipRule qSkipRule : qSkipRules) { QuestionSkipRule _qSkipRule = qSkipRule.clone(); for (AnswerSkipRule aSkipRule : qSkipRule.getAnswerSkipRules()) { AnswerSkipRule _aSkipRule = aSkipRule.clone(); _qSkipRule.addAnswerSkipRule(_aSkipRule); } _feSkipRule.addQuestionSkipRule(_qSkipRule); } to.setSkipRule(_feSkipRule); formElementDao.save(to); } public void moveSkips(FormElement from, FormElement to) { FormElementSkipRule feSkipRule = from.getSkipRule(); if (feSkipRule == null) return; from.removeSkipRule(); feSkipRule.setFormElement(to); to.setSkipRule(feSkipRule); formElementDao.save(from); formElementDao.save(to); } public FormElement getFantom(Long linkId) { LinkElement linkElement = (LinkElement) getFormElement(linkId); FormElement sElement = linkElement.getSourceElement(); FormElement fElement = null; if (sElement instanceof QuestionElement) { fElement = new QuestionElement(); } else if (sElement instanceof TableElement) { // TODO Improve TableElement tableElement = new TableElement(); tableElement.setTableType(((TableElement) sElement).getTableType()); List<TableColumn> tableColumns = ((TableElement) sElement).getTableColumns(); ArrayList<TableColumn> clonedTableColumns = new ArrayList<TableColumn>(); for (TableColumn tableColumn : tableColumns) { clonedTableColumns.add(tableColumn.clone()); } tableElement.setTableColumns(clonedTableColumns); fElement = tableElement; } else if (sElement instanceof ExternalQuestionElement) { fElement = new ExternalQuestionElement(); } else if (sElement instanceof ContentElement) { fElement = new ContentElement(); } FormElement.copy(sElement, fElement); fElement.setLearnMore(linkElement.getLearnMore()); fElement.setVisible(linkElement.isVisible()); fElement.setRequired(linkElement.isRequired()); fElement.setReadonly(linkElement.isReadonly()); fElement.setForm(linkElement.getForm()); fElement.setUuid(linkElement.getUuid()); fElement.setOrd(linkElement.getOrd()); fElement.setDescription(linkElement.getDescription()); if (fElement instanceof TableElement) { ((TableElement) fElement).setTableShortName(linkElement.getTableShortName()); } // fElement.setSkipRule(linkElement.getSkipRule()); FormElementSkipRule skipRule = linkElement.getSkipRule(); if (skipRule != null) { FormElementSkipRule newSkipRule = skipRule.clone(); List<QuestionSkipRule> skips = skipRule.getQuestionSkipRules(); //List<FormElementSkip> clonedSkips = new ArrayList<FormElementSkip>(); for (QuestionSkipRule skip : skips) { QuestionSkipRule clonedSkip = skip.clone(); clonedSkip.setDetails(skip.getDetails()); clonedSkip.setIdentifyingAnswerValue(skip.getIdentifyingAnswerValue()); List<AnswerSkipRule> answerSkipRules = skip.getAnswerSkipRules(); for (AnswerSkipRule answerSkipRule : answerSkipRules) { AnswerSkipRule _answerSkipRule = answerSkipRule.clone(); _answerSkipRule.setAnswerValue(answerSkipRule.getAnswerValue()); clonedSkip.addAnswerSkipRule(_answerSkipRule); } // skip.getDetails().getSkipTriggerQuestion().getId(); newSkipRule.addQuestionSkipRule(clonedSkip); } fElement.setSkipRule(newSkipRule); } /* preserve answerValue permanent_ids in order for skips to work. * if clone() method is used instead than it is not possible to match source answerValues to copied ones without the permanentId on the target * */ List<? extends BaseQuestion> sourceQuestions = sElement.getQuestions(); if (sourceQuestions != null && sourceQuestions.size() > 0) { for (BaseQuestion question : sourceQuestions) { BaseQuestion newQuestion = question.copy(); newQuestion.setSkipAffectees(question.getSkipAffectees()); newQuestion.setId(question.getId()); newQuestion.setUuid(question.getUuid()); Answer answer = question.getAnswer(); Answer newAnswer = answer.copy(); newAnswer.setUuid(answer.getUuid()); List<AnswerValue> answerValues = question.getAnswer().getAnswerValues(); newQuestion.setAnswer(newAnswer); for (AnswerValue answerValue : answerValues) { AnswerValue newAnswerValue = answerValue.clone(); //This is done to preserve the skips that might depend on this linkElement newAnswerValue.setPermanentId(answerValue.getPermanentId()); newAnswer.addAnswerValues(newAnswerValue); } if (sElement instanceof QuestionElement) { ((QuestionElement) fElement).setQuestion((Question) newQuestion); } else if (sElement instanceof TableElement) { ((TableElement) fElement).addQuestion((TableQuestion) newQuestion); } else if (sElement instanceof ExternalQuestionElement) { ((ExternalQuestionElement) fElement).setQuestion((ExternalQuestion) newQuestion); } } } return fElement; } public void modifyShortNames(List<FormElement> newElements) { //Make unique among this collection HashMap<String, Object> shortNamesMap = new HashMap<String, Object>(); for (FormElement formElement : newElements) { if (formElement instanceof TableElement) { TableElement tableElement = (TableElement) formElement; String tableShortName = com.healthcit.cacure.utils.StringUtils .prepareForShortName(tableElement.getTableShortName()); if (StringUtils.isBlank(tableShortName)) { tableShortName = "tableShortName"; } if (shortNamesMap.containsKey(tableShortName)) { int counter = 1; while (shortNamesMap.containsKey(tableShortName + counter)) { counter += 1; } shortNamesMap.put(tableShortName + counter, tableElement); } else { shortNamesMap.put(tableShortName, tableElement); } } List<? extends BaseQuestion> questions = formElement.getQuestions(); if (questions != null) { for (BaseQuestion baseQuestion : questions) { String shortName = com.healthcit.cacure.utils.StringUtils .prepareForShortName(baseQuestion.getShortName()); if (StringUtils.isBlank(shortName)) { shortName = "tableShortName"; } if (shortNamesMap.containsKey(shortName)) { int counter = 1; while (shortNamesMap.containsKey(shortName + counter)) { counter += 1; } shortNamesMap.put(shortName + counter, baseQuestion); } else { shortNamesMap.put(shortName, baseQuestion); } } } } if (shortNamesMap.isEmpty()) { return; } //Check similar short names in DB Set<String> similarShortNamesInDb = qstDao.getQuestionsShortNamesLike(shortNamesMap.keySet(), false); similarShortNamesInDb.addAll(teDao.getTableShortNamesLike(shortNamesMap.keySet(), false)); for (Entry<String, Object> entry : shortNamesMap.entrySet()) { String shortName = entry.getKey(); if (similarShortNamesInDb.contains(shortName)) { int counter = 1; while (similarShortNamesInDb.contains(shortName + counter)) { counter += 1; } shortName = shortName + counter; } if (entry.getValue() instanceof BaseQuestion) { ((BaseQuestion) entry.getValue()).setShortName(shortName); } else if (entry.getValue() instanceof TableElement) { ((TableElement) entry.getValue()).setTableShortName(shortName); } } } private List<String> collectAllShortNames(FormElement formElement) { ArrayList<String> shortNamesList = new ArrayList<String>(); if (formElement instanceof TableElement) { TableElement tableElement = (TableElement) formElement; if (tableElement.getTableShortName() != null) { shortNamesList.add(tableElement.getTableShortName()); } } List<? extends BaseQuestion> questions = formElement.getQuestions(); for (BaseQuestion baseQuestion : questions) { if (baseQuestion.getShortName() != null) { shortNamesList.add(baseQuestion.getShortName()); } } return shortNamesList; } public DuplicateResultBean hasShortNameDuplicates(FormElement formElement) { List<String> collectedShortNames = collectAllShortNames(formElement); return hasShortNameDuplicates(collectedShortNames); } public DuplicateResultBean hasShortNameDuplicates(List<String> collectedShortNames) { HashSet<String> uniqShortnamesSet = new HashSet<String>(collectedShortNames); HashSet<String> duplShortnamesSet = new HashSet<String>(); if (uniqShortnamesSet.size() != collectedShortNames.size()) { for (String uniqSn : uniqShortnamesSet) { collectedShortNames.remove(uniqSn); } duplShortnamesSet.addAll(collectedShortNames); } Set<String> exactQuestionsShortNames = qstDao.getQuestionsShortNamesLike(uniqShortnamesSet, true); duplShortnamesSet.addAll(exactQuestionsShortNames); Set<String> exactTableShortNames = teDao.getTableShortNamesLike(uniqShortnamesSet, true); duplShortnamesSet.addAll(exactTableShortNames); if (duplShortnamesSet.isEmpty()) { return new DuplicateResultBean(DuplicateResultType.OK, null); } else { return new DuplicateResultBean(DuplicateResultType.NOT_UNIQUE, duplShortnamesSet.toArray(new String[0])); } } }