Skip to content

Instantly share code, notes, and snippets.

@khaosans
Created September 21, 2015 16:47
Show Gist options
  • Save khaosans/7a42f1a4c1b5c274ccc1 to your computer and use it in GitHub Desktop.
Save khaosans/7a42f1a4c1b5c274ccc1 to your computer and use it in GitHub Desktop.
documentService impl
/*
* Copyright (c) 2005-2006 Jama Software, Inc. 600 NW 14th Avenue, Portland, Oregon 97209 U.S.A. All rights reserved.
*
* This software is the confidential and proprietary information of Jama Software, Inc. ("Confidential Information").
* You shall not disclose such Confidential Information and shall use it only in accordance with the terms of the
* license agreement you entered into with Jama Software.
*/
package com.jamasoftware.contour.service.impl;
import com.jamasoftware.contour.api.domain.ContourItem;
import com.jamasoftware.contour.api.domain.Document;
import com.jamasoftware.contour.api.domain.DocumentField;
import com.jamasoftware.contour.api.domain.DocumentGroup;
import com.jamasoftware.contour.api.domain.DocumentNode;
import com.jamasoftware.contour.api.domain.DocumentTypeCategory;
import com.jamasoftware.contour.api.domain.Document_Document;
import com.jamasoftware.contour.api.domain.Lookup;
import com.jamasoftware.contour.api.domain.PropertyEntry;
import com.jamasoftware.contour.api.domain.User;
import com.jamasoftware.contour.api.domain.Version;
import com.jamasoftware.contour.api.exception.DocumentNullOrInactiveException;
import com.jamasoftware.contour.api.exception.ValidationException;
import com.google.common.collect.Sets;
import com.jamasoftware.contour.api.domain.*;
import com.jamasoftware.contour.api.exception.*;
import com.jamasoftware.contour.api.qa.domain.TestCaseStep;
import com.jamasoftware.contour.api.qa.dto.TestCaseStepDTO;
import com.jamasoftware.contour.api.reuse.domain.ReusedItemSummary;
import com.jamasoftware.contour.api.service.ClientServiceSupport;
import com.jamasoftware.contour.api.service.CurrentUserFinder;
import com.jamasoftware.contour.api.service.dto.ContourItemDTO;
import com.jamasoftware.contour.api.service.dto.DistributionUsersAndGroupsDTO;
import com.jamasoftware.contour.api.service.dto.DocumentDTO;
import com.jamasoftware.contour.api.service.dto.property.DocumentPropertyDTO;
import com.jamasoftware.contour.api.service.dto.visitor.contourItemDTO.SaveContourItemDTOOptions;
import com.jamasoftware.contour.api.service.property.ProjectProperty;
import com.jamasoftware.contour.api.service.property.PropertyType;
import com.jamasoftware.contour.api.service.search.PageInfo;
import com.jamasoftware.contour.api.service.search.SearchResults;
import com.jamasoftware.contour.api.service.search.SorterList;
import com.jamasoftware.contour.api.util.DocumentFieldNames;
import com.jamasoftware.contour.api.util.ScopeRefWrap;
import com.jamasoftware.contour.dao.DocumentDao;
import com.jamasoftware.contour.dao.DocumentGroupDao;
import com.jamasoftware.contour.dao.DocumentNodeDao;
import com.jamasoftware.contour.dao.VersionDao;
import com.jamasoftware.contour.data.HibernateUtil;
import com.jamasoftware.contour.data.dao.GenericDao;
import com.jamasoftware.contour.data.util.search.indexer.Indexer;
import com.jamasoftware.contour.document.move.ItemMover;
import com.jamasoftware.contour.documentKey.SetKeyChanger;
import com.jamasoftware.contour.domain.EventEntryImpl;
import com.jamasoftware.contour.domain.EventTypeImpl;
import com.jamasoftware.contour.domain.ObjectTypeImpl;
import com.jamasoftware.contour.domain.visitor.contourItem.*;
import com.jamasoftware.contour.exception.HierarchyViolationException;
import com.jamasoftware.contour.qa.service.assembler.TestCaseStepAssembler;
import com.jamasoftware.contour.reuse.copier.ItemReuser;
import com.jamasoftware.contour.search.service.FilterService;
import com.jamasoftware.contour.service.ContourItemService;
import com.jamasoftware.contour.service.DocumentService;
import com.jamasoftware.contour.service.PropertyService;
import com.jamasoftware.contour.service.RelationshipService;
import com.jamasoftware.contour.service.assembler.DocumentDndHelper;
import com.jamasoftware.contour.service.assembler.DocumentTreeHelper;
import com.jamasoftware.contour.service.documentNode.DocumentNodeManager;
import com.jamasoftware.contour.service.documentNode.TreeChangeCommand;
import com.jamasoftware.contour.service.documentNode.TreeChangeResult;
import com.jamasoftware.contour.service.documentNode.TreeNodeChangeInfo;
import com.jamasoftware.contour.service.documentNode.cache.CachedDocumentNode;
import com.jamasoftware.contour.service.dto.ExtDocumentTreeNode;
import com.jamasoftware.contour.service.dto.FilterOrSmartFilterDTOHolder;
import com.jamasoftware.contour.service.dto.TreeOptions;
import com.jamasoftware.contour.service.event.system.BatchSystemEvent;
import com.jamasoftware.contour.service.event.system.SystemEvent;
import com.jamasoftware.contour.service.helper.ContourItemHelper;
import com.jamasoftware.contour.service.impl.helper.ItemReuseContext;
import com.jamasoftware.contour.service.impl.helper.PaginationHelper;
import com.jamasoftware.contour.service.impl.helper.validation.ContourItemValidationHelper;
import com.jamasoftware.contour.service.property.OrgProperties;
import com.jamasoftware.contour.util.*;
import com.jamasoftware.contour.util.hierarchy.HierarchyUtil;
import com.jamasoftware.contour.work.ThreadWorkProgress;
import com.jamasoftware.contour.work.WorkProgressTracker;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.ConstraintViolation;
import java.util.*;
import static java.util.Collections.EMPTY_LIST;
/**
* Default implementation of {@link DocumentService}.
*/
@Transactional
public class DocumentServiceImpl implements DocumentService, ApplicationContextAware {
private final Logger logger = Logger.getLogger(DocumentServiceImpl.class);
private static final List<String> GLOBAL_ID_OVERRIDE_LIST = Arrays.asList(DocumentFieldNames.FIELD_GLOBAL_ID);
private DocumentDao documentDao;
private RelationshipService relationshipService;
private DocumentGroupDao documentGroupDao;
private CurrentUserFinder currentUserFinder;
private VersionDao versionDao;
private DocumentDndHelper documentDndHelper;
private DocumentTreeHelper documentTreeHelper;
private PropertyService propertyService;
private ApplicationContext applicationContext;
private DocumentNodeDao documentNodeDao;
private DocumentNodeManager documentNodeManager;
private GenericDao genericDao;
private ContourItemHelper contourItemHelper;
private ContourItemService contourItemService;
private TestCaseStepAssembler testCaseStepAssembler;
private SetKeyChanger setKeyChanger;
private HierarchyUtil hierarchyUtil;
private ItemReuser itemReuser;
private FilterService filterService;
private ContourItemValidationHelper contourItemValidationHelper;
private ItemMover itemMover;
@Autowired private Indexer indexer;
@Autowired
private ClientServiceSupport clientServiceSupport;
@Override
public DocumentDTO getDocumentDto(Integer id) {
if (logger.isDebugEnabled()) {
logger.debug("getting document by id: " + id);
}
Document doc = documentDao.getDocument(id);
DocumentDTO dto = (DocumentDTO) doc.accept(ToDTOContourItemVisitor.INSTANCE, ToDTOContourItemOptions.create(true, null, true));
if (logger.isDebugEnabled()) {
logger.debug("found document: " + dto);
}
return dto;
}
@Override
public DocumentPropertyDTO getDocumentPropertyDTO(Integer id) {
Document doc = documentDao.getDocument(id);
String fullPath = documentNodeManager.getFullPath(doc);
return new DocumentPropertyDTO(doc, fullPath);
}
@Override
public Document getDocument(Integer id) {
return id == null ? null : documentDao.getDocument(id);
}
@Override
public List<Document> getDocumentByIds(List<Integer> docIds) {
return documentDao.getDocumentByIds(docIds);
}
@Override
public ReusedItemSummary copyDocuments(List<Integer> docIds, Map<String, String> params, Map<Integer, ScopeRefWrap> itemTypeToLocationMap, List<Integer> sourceIdsWithMissingTargets) {
String message = String.format("Copying doc %s", docIds.get(0));
if (docIds.size() > 1) {
message += " and " + (docIds.size() - 1) + " other documents";
}
int sessionId = JamaTimer.begin(message, Level.INFO);
ReusedItemSummary summary = itemReuser.copyDocuments(docIds, params, itemTypeToLocationMap, sourceIdsWithMissingTargets);
JamaTimer.end(sessionId, message, Level.INFO);
return summary;
}
@Override
public ReusedItemSummary syncDocuments(List<Integer> docIds, Map<String, String> params, List<Integer> sourceItemsWithMissingTargets, Map<Integer, ScopeRefWrap> itemTypeToLocationMap) {
String message = String.format("Syncing doc %s", docIds.get(0));
if (docIds.size() > 1) {
message += " and " + (docIds.size() - 1) + " other documents";
}
int sessionId = JamaTimer.begin(message);
ReusedItemSummary summary = itemReuser.syncDocuments(docIds, params, sourceItemsWithMissingTargets, itemTypeToLocationMap);
JamaTimer.end(sessionId, message);
return summary;
}
@Override
public ReusedItemSummary syncToBatchDocuments(List<Integer> sourceDocIds, List<Integer> copyToDocsIds, Map<String, String> params) {
WorkProgressTracker work = ThreadWorkProgress.get();
work.pushStep("Synchronizing to items", copyToDocsIds.size());
for (Integer docId : copyToDocsIds) {
work.addProgress();
params.put(ItemReuseContext.COPY_TARGET_ID, docId.toString());
params.put(ItemReuseContext.COPY_LOCATION_PARAM, "a-" + docId.toString());
copyDocuments(sourceDocIds, params, null, new ArrayList<Integer>(0));
}
work.popStep();
return null;
}
@Override
public ReusedItemSummary syncToBatchProjects(Integer sourceProjectId, List<Integer> copyToProjectIds, Map<String, String> params) {
WorkProgressTracker work = ThreadWorkProgress.get();
work.pushStep("Synchronizing to projects", copyToProjectIds.size());
List<Integer> docIdsToCopy = getTopLevelDocumentIdsForProject(sourceProjectId);
for (Integer projectId : copyToProjectIds) {
work.addProgress();
params.put(ItemReuseContext.COPY_LOCATION_PARAM, "p-" + projectId);
copyDocuments(docIdsToCopy, params, null, new ArrayList<Integer>(0));
}
work.popStep();
return null;
}
@Override
public List<Integer> getTopLevelDocumentIdsForProject(int projectId) {
CachedDocumentNode projectNode = DocumentNodeManager.getInstance().getCachedDocumentNode(Scope.PROJECT, projectId, true);
List<Integer> docIds = new ArrayList<Integer>();
if (projectNode != null) {
for (CachedDocumentNode child : projectNode.getChildren()) {
if (Scope.DOCUMENT.equals(child.getScopeId())) {
docIds.add(child.getRefId());
}
}
}
return docIds;
}
@Override
public ReusedItemSummary getReuseSummary(List<Integer> docIds, Map<String, String> params, Map<Integer, ScopeRefWrap> itemTypeToLocationMap) {
return itemReuser.previewCopyDocuments(docIds, params, itemTypeToLocationMap);
}
@Override
public Integer getReuseCount(List<Integer> docIds, Map<String, String> params, Map<Integer, ScopeRefWrap> itemTypeToLocationMap) {
return itemReuser.getReuseCount(docIds, params, itemTypeToLocationMap);
}
/**
* Deactivates (set active=false) a document or folder.
*
* @param documentId the document to delete
* @return list of errors such as relationships existing.
*/
@Override
public List<TreeNodeChangeInfo> deactivateDocument(Integer documentId) {
ContourItem item = contourItemService.getContourItem(documentId);
if (item == null) {
return new ArrayList<TreeNodeChangeInfo>(0);
}
logger.info("Deactivate doc " + documentId + ' ' + item.getName());
return item.accept(DeactivateContourItemVisitor.INSTANCE, DeactivateContourItemOptions.create(currentUserFinder.getCurrentUser()));
}
@Override
public List<TreeNodeChangeInfo> deactivateDocument(Document doc, User checkLockedByUser, Set<Integer> skipDocIds, Boolean indexNow) {
String flushMode = genericDao.doNotFlushTemporarily();
try {
if (skipDocIds == null) {
skipDocIds = new HashSet<Integer>(0);
}
User user = currentUserFinder.getCurrentUser();
List<SystemEvent> events = new ArrayList<SystemEvent>();
List<TreeNodeChangeInfo> numChanges = deactivateDocument(doc, true, checkLockedByUser, skipDocIds, indexNow);
List<ContourItem> deletedDocs = new ArrayList<ContourItem>();
for (TreeNodeChangeInfo changes : numChanges) {
if (changes.getDeleted() && Scope.DOCUMENT.equals(changes.getScopeId())) {
Document subDoc = documentDao.getDocument(changes.getRefId());
deletedDocs.add(subDoc);
events.add(new SystemEvent(this, ObjectTypeImpl.Document, changes.getRefId(), subDoc, user, EventEntryImpl.DOCUMENT_DELETE, doc.getProject(), EventTypeImpl.BATCH_DELETE, null, null));
}
}
int numDocsDeleted = deletedDocs.size();
if (numDocsDeleted == 0 && !DocumentTypeCategory.isCategory(doc.getDocumentType(), DocumentTypeCategory.ATTACHMENT)) {
return numChanges;
}
String comments = EventUtil.getMultiItemActivityComment("deleted", deletedDocs);
String details = "";
String action;
Integer type;
Integer objType;
if (numDocsDeleted > 1) {
action = EventEntryImpl.MULTIPLE_ITEMS_DELETED;
type = EventTypeImpl.BATCH_SUMMARY;
objType = ObjectTypeImpl.Anonymous;
details = EventUtil.getMultiItemEventDetails(deletedDocs);
}
else {
action = EventEntryImpl.DOCUMENT_DELETE;
type = EventTypeImpl.DELETE;
objType = ObjectTypeImpl.Document;
}
ContourItem deletedDoc = doc.getActive() ? deletedDocs.get(0) : doc;
SystemEvent se = new SystemEvent(this, objType, deletedDoc.getId(), deletedDoc, user, action, deletedDoc.getProject(), type, comments, details);
applicationContext.publishEvent(numDocsDeleted > 1 ? new BatchSystemEvent(se, events) : se);
return numChanges;
}
finally {
genericDao.resumeNormalFlushOperation(flushMode);
}
}
private List<TreeNodeChangeInfo> deactivateDocument(Document doc, boolean removeNode, User checkLockedByUser, Set<Integer> skipDocIds, Boolean indexNow) {
if (skipDocIds.contains(doc.getId())) {
return Collections.emptyList();
}
if (checkLockedByUser != null && !ItemLockUtil.canUserHaveLock(checkLockedByUser.getId(), doc)) {
return Collections.singletonList(TreeNodeChangeInfo.couldNotDelete(doc, doc.getDocumentKey() + " " + doc.getName() + " is locked"));
}
List<TreeNodeChangeInfo> allChanges = new ArrayList<TreeNodeChangeInfo>();
List<Document> childDocs = documentNodeManager.getChildDocumentList(doc.getId());
for (Document d : childDocs) {
allChanges.addAll(deactivateDocument(d, false, checkLockedByUser, skipDocIds, indexNow));
}
boolean hasChildLocked = false;
List<Integer> lockedChildren = new ArrayList<Integer>();
for (TreeNodeChangeInfo changeInfo : allChanges) {
if (Scope.DOCUMENT.equals(changeInfo.getScopeId()) && !changeInfo.getDeleted() && changeInfo.getTriedToDelete()) {
hasChildLocked = true;
lockedChildren.add(changeInfo.getRefId());
}
}
if (hasChildLocked) {
//remove nodes that can be deleted
for (Document child : childDocs) {
if (!lockedChildren.contains(child.getId())) {
allChanges.addAll(removeDocumentNode(child.getId(), indexNow));
}
}
allChanges.add(0, TreeNodeChangeInfo.couldNotDelete(doc, doc.getDocumentKey() + " " + doc.getName() + " has a locked child"));
}
else {
createDocumentVersion(doc, "Deleted item", false);
contourItemService.deactivateContourItem(doc);
if (removeNode) {
allChanges.addAll(removeDocumentNode(doc.getId(), indexNow));
}
}
return allChanges;
}
private List<TreeNodeChangeInfo> removeDocumentNode(Integer docId, Boolean indexNow) {
DocumentNode node = documentNodeDao.getDocumentNode(Scope.DOCUMENT, docId);
TreeChangeResult result = documentNodeManager.removeSubtree(node, indexNow);
return result.createChangeInfoList();
}
/**
* Called from front end to save or update a document.
*
* @param dto the dto with all possible values.
* @return the DocumentDTO for front end to use.
*/
@Override
public DocumentDTO saveDocumentDto(DocumentDTO dto, SaveContourItemDTOOptions options) {
boolean newDoc = isDTOForNewDocument(dto);
if (newDoc && dto.getAttachment() == null) {
hierarchyUtil.assertValidHierarchyForDocumentDTO(dto);
}
if (CollectionUtils.isNotEmpty(dto.getTestCaseSteps())) {
for (TestCaseStepDTO tcsdto : dto.getTestCaseSteps()) {
if (newDoc || !dto.getId().equals(tcsdto.getTestCaseId())) {
tcsdto.setId(null);
}
tcsdto.setTestCaseId(dto.getId());
}
}
DocumentDTO docDto = contourItemService.saveContourItemDto(dto, options);
if (newDoc) {
Document document = (Document) contourItemService.getContourItem(docDto.getId());
if (CollectionUtils.isNotEmpty(dto.getTestCaseSteps())) {
List<TestCaseStep> steps = testCaseStepAssembler.toDomainList(dto.getTestCaseSteps());
document.getTestCaseSteps().clear();
for (TestCaseStep step : steps) {
step.setTestCase(document);
document.getTestCaseSteps().add(step);
}
contourItemService.saveContourItem(document);
docDto.setTestCaseSteps(testCaseStepAssembler.toDTOList(document.getTestCaseSteps()));
}
documentNodeManager.createDocumentNodeForDocument(document, dto.getParentId(), dto.getPlaceAfterId(), null);
}
return docDto;
}
@Override
public DocumentDTO verifyAndSaveDocumentDTO(DocumentDTO dto, SaveContourItemDTOOptions options) {
contourItemValidationHelper.setMissingValuesAndValidateDTO(dto, options.allowGlobalIdChange() ? GLOBAL_ID_OVERRIDE_LIST : EMPTY_LIST);
moveDocumentIfParentHasChanged(dto);
return saveDocumentDto(dto, options);
}
@Override
public String moveDocumentToProjectAsynchronously(Integer itemId, Integer newParentId, Integer newProjectId) {
ContourItem originalDoc = contourItemService.getContourItem(itemId);
Integer oldDocProjectId = originalDoc.getProject().getId();
if (newProjectId != null && !oldDocProjectId.equals(newProjectId)) {
assertNewParentIdForProjectMove(newParentId);
moveToNewProjectValidation(itemId,newProjectId);
return itemMover.moveItem(itemId, newProjectId);
}
throw new ValidationException("move within same project not supported");
}
private void assertNewParentIdForProjectMove(Integer newParentId) {
if(newParentId != null) {
throw new ValidationException("new parent item not supported when moving to another project");
}
}
private void moveDocumentIfParentHasChanged(DocumentDTO dto) {
if (!isDTOForNewDocument(dto)) {
DocumentNode docNode = documentNodeDao.getDocumentNode(Scope.DOCUMENT, dto.getId());
Integer oldParentId = docNode.getParentNode().getRefId();
Integer parentId = dto.getParentId();
if (!oldParentId.equals(parentId)) {
ContourItem originalDoc = contourItemService.getContourItem(dto.getId());
moveDocument(originalDoc, docNode, parentId);
}
}
}
private void moveToNewProjectValidation(Integer itemToMove, Integer projectToMoveTo) {
List<ValidationFailure> listOfValidationFailures = new LinkedList<>();
List<Integer> itemsUnableToWrite;
List<Integer> itemsLocked;
List<Integer> itemsWithDuplicateGlobalIds;
List<Integer> listOfParentAndChildrenItems = documentNodeManager.getFullChildDocumentIdList(itemToMove);
listOfParentAndChildrenItems.add(itemToMove);
assertRootItemIsComponentOrSetAndIsActive(itemToMove);
clientServiceSupport.assertUserCanWriteProject(projectToMoveTo);
itemsUnableToWrite = checkUserCanWriteItems(listOfParentAndChildrenItems);
itemsLocked = checkItemsNotLocked(listOfParentAndChildrenItems);
itemsWithDuplicateGlobalIds = checkDuplicateGlobalIds(listOfParentAndChildrenItems,projectToMoveTo);
if(!itemsLocked.isEmpty() ) {
ItemsLockedValidationFailure itemsLockedValidationFailure = new ItemsLockedValidationFailure("Items are locked",itemsLocked);
listOfValidationFailures.add(itemsLockedValidationFailure);
}
if(!itemsUnableToWrite.isEmpty()) {
ItemsPermissionsNotAuthorized itemsPermissionsNotAuthorized = new ItemsPermissionsNotAuthorized("Items don't have write permissions", itemsUnableToWrite);
listOfValidationFailures.add(itemsPermissionsNotAuthorized);
}
if(!itemsWithDuplicateGlobalIds.isEmpty()){
ItemsDuplicateGlobalIdException itemsDuplicateGlobalIdException = new ItemsDuplicateGlobalIdException("Items are duplicate global Ids", itemsWithDuplicateGlobalIds);
listOfValidationFailures.add(itemsDuplicateGlobalIdException);
}
if(!listOfValidationFailures.isEmpty()) {
throw new ValidationException("Cannot move Items", listOfValidationFailures);
}
}
private List<Integer> checkDuplicateGlobalIds(List<Integer> listToCheck, Integer projectIdToMoveTo){
PropertyEntry propertyEntry = propertyService.getOrganizationProperty(OrgProperties.ORGANIZATION_ALLOW_NONUNIQUE_GLOBALID_IN_PROJECTS, getOrganizationId());
Boolean isGlobalAllowedInProject = PropertyType.BOOLEAN.convertPropertyEntryValue(propertyEntry);
if(!isGlobalAllowedInProject){
return documentDao.getItemsWithSameGlobalIdWithInProject(listToCheck, projectIdToMoveTo);
}else {
return Collections.emptyList();
}
}
private void assertRootItemIsComponentOrSetAndIsActive(Integer itemToMove){
Document document = documentDao.getDocument(itemToMove);
if(!document.getActive()){
throw new ValidationException("Selected item to move is not active");
}
if(document.getDocumentType().getCategory() == null){
throw new ValidationException("The item you are trying to move is not a set or component");
}
if(!document.getDocumentType().getCategory().getDbId().equals(DocumentTypeCategory.COMPONENT.getDbId())
&& !document.getDocumentType().getCategory().getDbId().equals(DocumentTypeCategory.SET.getDbId()) ){
throw new ValidationException("The item you are trying to move is not a set or component");
}
}
private List<Integer> checkItemsNotLocked(List<Integer> listOfItems){
return documentDao.getLockedItems(listOfItems);
}
private List<Integer> checkUserCanWriteItems(List<Integer> listOfItems){
List<Integer> listOfNonWritableItems = new LinkedList<>();
List<Integer> listOfDocumentKeys = new LinkedList<>();
List<Document> listOfDocuments;
List<Integer> activeListOfDocuments = documentDao.getActiveDocumentsFromList(listOfItems);
for(Integer item: activeListOfDocuments){
try {
clientServiceSupport.assertUserCanWriteContourItem(item);
}catch (org.springframework.security.access.AccessDeniedException permissionException){
listOfNonWritableItems.add(item);
}
}
if(listOfNonWritableItems.size()>0){
listOfDocuments = documentDao.getDocumentByIds(listOfItems);
for(Document document: listOfDocuments){
listOfDocumentKeys.add(document.getId());
}
return listOfDocumentKeys;
} else {
return Collections.emptyList();
}
}
private void moveDocument(ContourItem originalDoc, DocumentNode docNode, Integer parentId) {
ContourItem parentDoc = parentId == null ? null : contourItemService.getContourItem(parentId);
if (!HierarchyUtil.isValidDocumentChild(parentDoc, originalDoc.getDocumentType(), null)) {
throw new HierarchyViolationException("The item with ID " + parentId + " is an invalid location");
}
Integer scopeId = parentDoc == null ? Scope.PROJECT : Scope.DOCUMENT;
Integer refId = parentDoc == null ? originalDoc.getProject().getId() : parentId;
DocumentNode newParentNode = documentNodeDao.getDocumentNode(scopeId, refId);
documentNodeManager.moveNodes(Collections.singletonList(docNode), newParentNode, null, TreeChangeCommand.getAllCommand());
}
/**
*
* @param dto the dto with all possible values.
* @return the DocumentDTO for front end to use.
* @throws Exception
*/
@Override
public DocumentDTO saveIncrementDocumentDto(DocumentDTO dto, SaveContourItemDTOOptions options) throws Exception {
if (dto.getTestCaseSteps() != null) {
for (TestCaseStepDTO tcsdto : dto.getTestCaseSteps()) {
tcsdto.setTestCaseId(dto.getId());
}
}
return contourItemService.saveIncrementContourItemDto(dto, options);
}
private boolean isDTOForNewDocument(DocumentDTO dto) {
return (dto.getId() == null || dto.getId() == 0);
}
/**
* Creates an activity entry for a document. This is used after
* a user does one or more incremental saves and then cancels
* out of the window.
*
* @param docId id of the document to create the event for
*/
@Override
public void publishDocEventAfterCancel(Integer docId) {
Version version = versionDao.getVersionByDocumentId(docId);
if (version != null && version.getOriginDocument().getId().equals(docId)) {
User user = currentUserFinder.getCurrentUser();
SystemEvent se = new SystemEvent(this, ObjectTypeImpl.Document, docId, version.getDocument(), user, EventEntryImpl.MODIFIED, version.getDocument().getProject(), EventTypeImpl.UPDATE, version.getComments(), "");
applicationContext.publishEvent(se);
}
}
@Override
public DocumentDTO saveDocumentDtoKeepVersion(DocumentDTO dto) throws Exception {
Document doc = documentDao.getDocument(dto.getId());
if (doc == null) {
return null;
}
doc.accept(MergeDtoIntoContourItemVisitor.INSTANCE, dto);
documentDao.saveDocument(doc);
return (DocumentDTO) doc.accept(ToDTOContourItemVisitor.INSTANCE, null);
}
@Override
public void saveDocumentWithComment(Document doc, String versionComment, Boolean shouldVersion) {
documentDao.saveDocument(doc);
if (Boolean.TRUE.equals(shouldVersion)) {
createDocumentVersion(doc, versionComment, true);
}
}
@Override
public ContourItemDTO updateDocumentFromItemWrapper(ItemWrapper itemWrapper, DistributionUsersAndGroupsDTO distributionList, String userComment, SaveContourItemDTOOptions options) {
return contourItemService.updateContourItemFromItemWrapper(itemWrapper, distributionList, userComment, options);
}
@Override
public ContourItem updateDocumentFromItemWrapper(ItemWrapper itemWrapper, DistributionUsersAndGroupsDTO distributionList, String userComment, SaveContourItemDTOOptions options, boolean updateSyncRelationships) {
return contourItemService.updateContourItemFromItemWrapper(itemWrapper, distributionList, userComment, options, updateSyncRelationships);
}
@Override
public Version createDocumentVersion(ContourItem document, String versionComments, boolean newDoc) {
return createDocumentVersion(document, versionComments, null, newDoc);
}
@Override
public Version createDocumentVersion(ContourItem document, String versionComments, String userComment, boolean newDoc) {
return createDocumentVersion(document, versionComments, userComment, newDoc, currentUserFinder.getCurrentUser());
}
@Override
public Version createDocumentVersion(ContourItem document, String versionComments, String userComment, boolean newDoc, User user) {
return contourItemHelper.createContourItemVersion(document, versionComments, userComment, newDoc, user);
}
@Override
public Version createVersionForDocumentWithout(ContourItem doc) {
return createDocumentVersion(doc, "Auto-created version", true);
}
private Boolean getShowIdInTree(Integer projectId) {
Boolean showIdInTree = Boolean.FALSE;
PropertyEntry config = propertyService.getProjectProperty(projectId, ProjectProperty.PROJECT_SHOW_ID_IN_TREE.getName());
if (config != null) {
showIdInTree = PropertyType.BOOLEAN.convertPropertyEntryValue(config, false);
}
return showIdInTree;
}
private static final Integer MAX_NODES = 250;
private Integer getMaxTreeNodes(Integer projectId) {
Integer maxNodes = MAX_NODES;
if (projectId != null) {
PropertyEntry config = propertyService.getProjectProperty(projectId, ProjectProperty.PROJECT_MAX_TREE_NODES.getName());
maxNodes = PropertyType.INTEGER.convertPropertyEntryValue(config, maxNodes);
}
return maxNodes;
}
@Override
public ExtDocumentTreeNode getExtTreeNodeForDocument(Integer docId, TreeOptions options) {
Document doc = documentDao.getDocument(docId);
if (options == null) {
options = new TreeOptions();
}
return documentTreeHelper.documentToNode(doc, options, null, true, null, false);
}
/**
* called by explorer and enhanced release trees on initial and subsequent node ticks
*/
@Override
public List<ExtDocumentTreeNode> getExtTreeNodes(ExtDocumentTreeNode node, TreeOptions options) throws Exception {
Integer projectId = options.getProjectId();
if (options.getMaxNodes() == null || options.getMaxNodes() <= 0) {
options.setMaxNodes(getMaxTreeNodes(projectId));
}
if (options.getShowKey() == null) {
boolean showId = (projectId == null) ? false : getShowIdInTree(projectId);
options.setShowKey(showId);
}
return documentTreeHelper.getExtTreeNodes(node, options);
}
@Override
public List<ExtDocumentTreeNode> getOutOfSyncTree(ExtDocumentTreeNode node, TreeOptions options) throws Exception {
String previousFlushMode = genericDao.doNotFlushTemporarily();
Integer projectId = options.getProjectId();
if (options.getMaxNodes() == null || options.getMaxNodes() <= 0) {
Integer maxNodes = (projectId == null) ? MAX_NODES : getMaxTreeNodes(projectId);
options.setMaxNodes(maxNodes);
}
if (options.getShowKey() == null) {
boolean showId = (projectId == null) ? false : getShowIdInTree(projectId);
options.setShowKey(showId);
}
List<ExtDocumentTreeNode> list = documentTreeHelper.getExtTreeNodes(node, options);
genericDao.flush();
genericDao.resumeNormalFlushOperation(previousFlushMode);
return list;
}
@Override
public String getPath(Integer documentId) {
CachedDocumentNode node = documentNodeManager.getCachedDocumentNode(Scope.DOCUMENT, documentId, true);
if (node == null) {
throw new RuntimeException("Item not found in tree!");
}
return node.getExtTreePath();
}
@Override
public void sortDocumentHeadingsByProject(Integer projectId) {
DocumentNodeManager.getInstance().regenerateStateAndFlushToDatabase(projectId, true);
}
@Override
public void moveItems(List<String> nodeList, String newParentId, String siblingId) {
String flushMode = genericDao.doNotFlushTemporarily();
try {
documentDndHelper.moveItems(nodeList, newParentId, siblingId, this);
}
finally {
genericDao.resumeNormalFlushOperation(flushMode);
}
}
@Override
public void moveItemsInFilter(FilterOrSmartFilterDTOHolder filter, String newParentId, String siblingId) {
List<Integer> ids = filterService.getItemIdsFromFilter(filter);
List<String> nodeList = new ArrayList<>(ids.size());
for(Integer id : ids) {
nodeList.add(ExtDocumentTreeNode.buildTreeNodeId(ExtDocumentTreeNode.TreeNodeKey.DOCUMENT, id));
}
moveItems(nodeList, newParentId, siblingId);
}
@Override
public boolean hasPendingChanges(DocumentDTO updContourDocDTO) {
return contourItemService.hasPendingChanges(updContourDocDTO);
}
/**
* Given a document and group, fetch the downstream related documents
*/
@Override
public List<DocumentDTO> getDownStreamRelatedDocuments(Integer documentId, Integer groupId) {
if (logger.isDebugEnabled()) {
logger.debug("getting downstream related documents for document: " + documentId + " and group" + groupId);
}
List<DocumentDTO> documents = new ArrayList<DocumentDTO>();
DocumentGroup group = documentGroupDao.getDocumentGroup(groupId);
if (group != null) {
boolean isForward = true;
List<Document_Document> relatedDocuments = relationshipService.getRelationshipsForDocument(documentId, isForward);
for (Document_Document dd : relatedDocuments) {
if (dd.getToDocument().getDocumentType().equals(group.getDocumentType())) {
ContourItem d = dd.getToDocument();
d = HibernateUtil.deproxy(d, ContourItem.class);
//TODO: When we allow relationships to other ContourItems, we should move this
// method of DocumentService and not perform this type checking
if (d instanceof Document) {
DocumentDTO ddto = (DocumentDTO) d.accept(ToDTOContourItemVisitor.INSTANCE, null);
documents.add(ddto);
if (logger.isDebugEnabled()) {
logger.debug(" - related document: " + d);
logger.debug(" - related document dto: " + ddto);
}
}
}
}
}
return documents;
}
/**
* given a parent release document and a group, fetch the paged downstream relationship documents for that group
*/
@Override
public SearchResults<DocumentDTO> getPagedDownStreamDocuments(int documentId, int groupId, int start, int count, String orderBy) {
if (logger.isDebugEnabled()) {
logger.debug("getting paged downstream documents for document: " + documentId + " and group: " + groupId);
}
SorterList sorter = HibernateUtil.createSorterList(orderBy);
PageInfo pageInfo = HibernateUtil.createPageInfo(start, count);
List<DocumentDTO> relatedDocuments = this.getDownStreamRelatedDocuments(documentId, groupId);
return PaginationHelper.createSearchResults(new ArrayList<DocumentDTO>(relatedDocuments), sorter, pageInfo, DocumentDTO.class);
}
@Override
public void ensureDocumentsHaveVersion() {
List<Document> docsWithoutVersions = versionDao.getDocumentsWithoutVersion();
List<Document> createdVersionDocs = new ArrayList<Document>(docsWithoutVersions.size());
for (Document docWithoutVersion : docsWithoutVersions) {
createVersionForDocumentWithout(docWithoutVersion);
createdVersionDocs.add(docWithoutVersion);
}
genericDao.flush();
for (Document doc : createdVersionDocs) {
genericDao.detatchObject(doc.getDocumentType());
genericDao.detatchObject(doc.getCurrentVersion());
genericDao.detatchObject(doc);
}
}
@Override
public void changeSetKey(Integer setId, String newSetKey, Boolean updateChildItems) {
setKeyChanger.changeSetKey(setId, newSetKey, updateChildItems);
}
@Override
public int getTotalProjectItems(int projectId) {
return documentDao.getTotalProjectItems(projectId);
}
@Override
public void replaceLookupById(Integer originalLookupId, Lookup newLookup) {
Integer orgId = newLookup.getOrganizationId();
List<DocumentField> docLookupFields = documentDao.getDocumentLookupFields(orgId);
for (DocumentField dField : docLookupFields) {
if (Boolean.TRUE.equals(dField.getCustom())) {
documentDao.replaceCustomLookupById(originalLookupId, newLookup, dField);
}
else {
documentDao.replacePredefinedLookupById(originalLookupId, newLookup, dField);
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
private Integer getOrganizationId() {
return currentUserFinder.getCurrentUser() != null ? currentUserFinder.getCurrentUser().getOrganizationId() : null;
}
public void setVersionDao(VersionDao versionDao) {
this.versionDao = versionDao;
}
public void setCurrentUserFinder(CurrentUserFinder currentUserFinder) {
this.currentUserFinder = currentUserFinder;
}
public void setDocumentDao(DocumentDao documentDao) {
this.documentDao = documentDao;
}
public void setDocumentGroupDao(DocumentGroupDao documentGroupDao) {
this.documentGroupDao = documentGroupDao;
}
public void setPropertyService(PropertyService propertyService) {
this.propertyService = propertyService;
}
public void setDocumentDndHelper(DocumentDndHelper documentDndHelper) {
this.documentDndHelper = documentDndHelper;
}
public void setDocumentTreeHelper(DocumentTreeHelper documentTreeHelper) {
this.documentTreeHelper = documentTreeHelper;
}
public void setRelationshipService(RelationshipService relationshipService) {
this.relationshipService = relationshipService;
}
public void setDocumentNodeDao(DocumentNodeDao documentNodeDao) {
this.documentNodeDao = documentNodeDao;
}
public void setDocumentNodeManager(DocumentNodeManager documentNodeManager) {
this.documentNodeManager = documentNodeManager;
}
public void setGenericDao(GenericDao genericDao) {
this.genericDao = genericDao;
}
public void setContourItemHelper(ContourItemHelper contourItemHelper) {
this.contourItemHelper = contourItemHelper;
}
public void setContourItemService(ContourItemService contourItemService) {
this.contourItemService = contourItemService;
}
public void setSetKeyChanger(SetKeyChanger setKeyChanger) {
this.setKeyChanger = setKeyChanger;
}
public void setTestCaseStepAssembler(TestCaseStepAssembler testCaseStepAssembler) {
this.testCaseStepAssembler = testCaseStepAssembler;
}
public void setHierarchyUtil(HierarchyUtil hierarchyUtil) {
this.hierarchyUtil = hierarchyUtil;
}
public void setItemReuser(ItemReuser itemReuser) {
this.itemReuser = itemReuser;
}
public void setFilterService(FilterService filterService) {
this.filterService = filterService;
}
public void setContourItemValidationHelper(ContourItemValidationHelper contourItemValidationHelper) {
this.contourItemValidationHelper = contourItemValidationHelper;
}
public void setItemMover(ItemMover itemMover) {
this.itemMover = itemMover;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment