/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.client.ui.rcp.controllers.dnd.objectmapping;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jubula.client.core.businessprocess.CompNameManager;
import org.eclipse.jubula.client.core.businessprocess.CompNameTypeManager;
import org.eclipse.jubula.client.core.businessprocess.ComponentNamesBP;
import org.eclipse.jubula.client.core.businessprocess.IWritableComponentNameCache;
import org.eclipse.jubula.client.core.events.DataEventDispatcher;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.DataState;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.UpdateState;
import org.eclipse.jubula.client.core.model.IComponentNamePO;
import org.eclipse.jubula.client.core.model.IObjectMappingAssoziationPO;
import org.eclipse.jubula.client.core.model.IObjectMappingCategoryPO;
import org.eclipse.jubula.client.core.model.PoMaker;
import org.eclipse.jubula.client.ui.rcp.editors.ObjectMappingMultiPageEditor;
import org.eclipse.jubula.client.ui.rcp.i18n.Messages;
import org.eclipse.jubula.client.ui.utils.DialogUtils;
import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.i18n.CompSystemI18n;
import org.eclipse.jubula.tools.internal.xml.businessmodell.CompSystem;
import org.eclipse.jubula.tools.internal.xml.businessmodell.Component;
import org.eclipse.osgi.util.NLS;
/**
* Utility class containing methods for use in drag and drop as well as
* cut and paste operations in the Object Mapping Editor.
*
* @author BREDEX GmbH
* @created 20.03.2008
*/
public class OMEditorDndSupport {
/**
* Private constructor
*/
private OMEditorDndSupport() {
// Do nothing
}
/**
* Assigns the given Component Names to the given association.
*
* @param compNamesToMove The Component Names to assign.
* @param target The association to which the Component Names will be
* assigned.
* @param editor Editor in which the assignment is taking place.
* @return whether the operation is cancelled by the user
*/
public static boolean checkTypeCompatibilityAndMove(
List<IComponentNamePO> compNamesToMove,
IObjectMappingAssoziationPO target,
ObjectMappingMultiPageEditor editor) {
IWritableComponentNameCache compCache = editor.getCompNameCache();
IObjectMappingCategoryPO unmappedTechnical =
editor.getAut().getObjMap().getUnmappedTechnicalCategory();
if (checkProblemsAndStop(compNamesToMove, target, editor)) {
return true;
}
for (IComponentNamePO compName : compNamesToMove) {
String compNameGuid = compName.getGuid();
if (!target.getLogicalNames().contains(compNameGuid)) {
IObjectMappingAssoziationPO oldAssoc =
editor.getOmEditorBP().getAssociation(compNameGuid);
compCache.changeReuse(target, null, compNameGuid);
compCache.changeReuse(oldAssoc, compNameGuid, null);
if (getSection(target).equals(
unmappedTechnical)) {
// Change section to mapped, creating new categories
// if necessary.
IObjectMappingCategoryPO mapped =
editor.getAut().getObjMap().getMappedCategory();
IObjectMappingCategoryPO newCategory =
editor.getOmEditorBP().createCategory(
mapped, target.getCategory());
target.getCategory().removeAssociation(target);
newCategory.addAssociation(target);
}
cleanupAssociation(editor, oldAssoc);
}
}
DataEventDispatcher.getInstance().fireDataChangedListener(
editor.getAut().getObjMap(),
DataState.StructureModified,
UpdateState.onlyInEditor);
editor.getTreeViewer().setExpandedState(target, true);
return false;
}
/**
* Checks whether mapping the CNs to the Technical Name creates any problems
* if yes, the user is notified and can cancel the action
* @param compNamesToMove The Component Names to assign.
* @param target The association to which the Component Names will be
* assigned.
* @param editor Editor in which the assignment is taking place.
* @return whether the operation is cancelled by the user
*/
private static boolean checkProblemsAndStop(
List<IComponentNamePO> compNamesToMove,
IObjectMappingAssoziationPO target,
ObjectMappingMultiPageEditor editor) {
List<IComponentNamePO> problems = new ArrayList<>();
List<Component> availableComponents = ComponentBuilder.getInstance()
.getCompSystem().getComponents(editor.getAut().getToolkit(),
true);
String type = null;
if (target.getTechnicalName() != null) {
type = CompSystem.getComponentType(target.getTechnicalName()
.getSupportedClassName(), availableComponents);
}
IComponentNamePO masterCN;
for (IComponentNamePO cN : compNamesToMove) {
masterCN = CompNameManager.getInstance()
.getResCompNamePOByGuid(cN.getGuid());
if (masterCN == null) {
continue;
}
if (!masterCN.getUsageType().equals(
ComponentNamesBP.UNKNOWN_COMPONENT_TYPE)
&& !CompNameTypeManager.doesFirstTypeRealizeSecond(
type, masterCN.getUsageType())) {
problems.add(masterCN);
}
}
if (problems.isEmpty()) {
return false;
}
StringBuilder list = new StringBuilder();
for (IComponentNamePO cN : problems) {
list.append(StringConstants.SPACE);
list.append(StringConstants.SPACE);
list.append(StringConstants.SPACE);
list.append(cN.getName());
list.append(StringConstants.SPACE);
list.append(StringConstants.LEFT_BRACKET);
list.append(CompSystemI18n.getString(cN.getUsageType()));
list.append(StringConstants.RIGHT_BRACKET);
list.append(StringConstants.NEWLINE);
}
String message = NLS.bind(Messages.IncompatibleMapDialogText,
list.toString());
MessageDialog dialog = new MessageDialog(null,
Messages.IncompatibleMapDialogTitle,
null,
message, MessageDialog.QUESTION, new String[] {
Messages.DialogMessageButton_YES,
Messages.DialogMessageButton_NO }, 0);
dialog.create();
DialogUtils.setWidgetNameForModalDialog(dialog);
dialog.open();
return dialog.getReturnCode() != 0;
}
/**
* Performs any necessary "cleanup" on an Object Mapping Association. This
* includes moving the association to the appropriate "section"
* (ex. Unmapped Technical Components or Mapped Components).
*
* @param editor The editor in which the cleanup is to occur.
* @param assoc The association to cleanup.
*/
private static void cleanupAssociation(ObjectMappingMultiPageEditor editor,
IObjectMappingAssoziationPO assoc) {
if (assoc != null
&& assoc.getLogicalNames().isEmpty()) {
IObjectMappingCategoryPO fromCategory =
assoc.getCategory();
if (assoc.getTechnicalName() != null) {
// Change section to unmapped tech, creating new
// categories if necessary.
IObjectMappingCategoryPO unmappedTech =
editor.getAut().getObjMap()
.getUnmappedTechnicalCategory();
IObjectMappingCategoryPO newCategory =
editor.getOmEditorBP().createCategory(
unmappedTech, fromCategory);
fromCategory.removeAssociation(assoc);
newCategory.addAssociation(assoc);
} else {
// Association has no logical names and no technical
// name. It should be deleted.
fromCategory.removeAssociation(assoc);
}
}
}
/**
* Moves the given Component Names to the given category. This removes
* whatever mappings in which the Component Names were involved in the
* context of the supported editor.
*
* @param compNamesToMove The Component Names to move.
* @param target The category to which the Component Names will be
* moved.
* @param editor Editor in which the move is taking place.
*/
public static void checkTypeCompatibilityAndMove(
List<IComponentNamePO> compNamesToMove,
IObjectMappingCategoryPO target,
ObjectMappingMultiPageEditor editor) {
IObjectMappingCategoryPO unmappedCat =
editor.getAut().getObjMap().getUnmappedLogicalCategory();
IObjectMappingCategoryPO targetSection = getSection(target);
if (!unmappedCat.equals(targetSection)) {
return;
}
IWritableComponentNameCache compCache = editor.getCompNameCache();
for (IComponentNamePO compName : compNamesToMove) {
String compNameGuid = compName.getGuid();
IObjectMappingAssoziationPO oldAssoc =
editor.getOmEditorBP().getAssociation(compNameGuid);
if (oldAssoc.getTechnicalName() == null) {
oldAssoc.getCategory().removeAssociation(oldAssoc);
target.addAssociation(oldAssoc);
continue;
}
IObjectMappingAssoziationPO newAssoc =
PoMaker.createObjectMappingAssoziationPO(
null, new HashSet<String>());
editor.getAut().getObjMap().addAssociationToCache(newAssoc);
compCache.changeReuse(newAssoc, null, compNameGuid);
compCache.changeReuse(oldAssoc, compNameGuid, null);
target.addAssociation(newAssoc);
if (oldAssoc != null) {
if (oldAssoc.getLogicalNames().isEmpty()) {
// Change section to unmapped tech, creating new
// categories if necessary.
IObjectMappingCategoryPO unmappedTech =
editor.getAut().getObjMap()
.getUnmappedTechnicalCategory();
IObjectMappingCategoryPO newCategory =
editor.getOmEditorBP().createCategory(
unmappedTech, oldAssoc.getCategory());
oldAssoc.getCategory().removeAssociation(oldAssoc);
newCategory.addAssociation(oldAssoc);
}
}
}
DataEventDispatcher.getInstance().fireDataChangedListener(
editor.getAut().getObjMap(),
DataState.StructureModified,
UpdateState.onlyInEditor);
editor.getTreeViewer().refresh(target);
editor.getTreeViewer().setExpandedState(target, true);
}
/**
* Moves the given associations to the given category.
*
* @param toMove The associations to move.
* @param target The category into which the associations will be moved.
* @param editor The editor in which the move is occurring.
*/
public static void checkAndMoveAssociations(
List<IObjectMappingAssoziationPO> toMove,
IObjectMappingCategoryPO target,
ObjectMappingMultiPageEditor editor) {
IObjectMappingCategoryPO unmappedTechNames =
editor.getAut().getObjMap().getUnmappedTechnicalCategory();
IObjectMappingCategoryPO newSection = getSection(target);
for (IObjectMappingAssoziationPO assoc : toMove) {
IObjectMappingCategoryPO oldSection = getSection(assoc);
if (oldSection.equals(newSection)) {
IObjectMappingCategoryPO fromCategory = assoc.getCategory();
fromCategory.removeAssociation(assoc);
target.addAssociation(assoc);
continue;
} else if (unmappedTechNames.equals(newSection)) {
IObjectMappingCategoryPO unmappedCompNames =
editor.getAut().getObjMap().getUnmappedLogicalCategory();
IWritableComponentNameCache compCache =
editor.getCompNameCache();
for (String compNameGuid
: new ArrayList<String>(assoc.getLogicalNames())) {
compCache.changeReuse(assoc, compNameGuid, null);
IObjectMappingAssoziationPO compNameAssoc =
PoMaker.createObjectMappingAssoziationPO(
null, new HashSet<String>());
editor.getAut().getObjMap()
.addAssociationToCache(compNameAssoc);
compCache.changeReuse(
compNameAssoc, null, compNameGuid);
unmappedCompNames.addAssociation(compNameAssoc);
}
IObjectMappingCategoryPO fromCategory = assoc.getCategory();
fromCategory.removeAssociation(assoc);
target.addAssociation(assoc);
}
}
if (!toMove.isEmpty()) {
DataEventDispatcher.getInstance().fireDataChangedListener(
editor.getAut().getObjMap(),
DataState.StructureModified,
UpdateState.onlyInEditor);
editor.getTreeViewer().setExpandedState(target, true);
}
}
/**
*
* @param toMove The associations for which the move operation is
* to be checked.
* @param target The target category for which the move operation is to be
* checked.
* @param editor The editor in which the move operation would occur.
* @return <code>true</code> if the arguments represent a valid move
* operation. Otherwise <code>false</code>.
*/
public static boolean canMoveAssociations(
List<IObjectMappingAssoziationPO> toMove,
IObjectMappingCategoryPO target,
ObjectMappingMultiPageEditor editor) {
for (IObjectMappingAssoziationPO assoc : toMove) {
if (!canMove(assoc, target, editor)) {
return false;
}
}
return true;
}
/**
*
* @param assoc The association for which the move operation is
* to be checked.
* @param target The target category for which the move operation is to be
* checked.
* @param editor The editor in which the move operation would occur.
* @return <code>true</code> if the arguments represent a valid move
* operation. Otherwise <code>false</code>.
*/
private static boolean canMove(IObjectMappingAssoziationPO assoc,
IObjectMappingCategoryPO target,
ObjectMappingMultiPageEditor editor) {
IObjectMappingCategoryPO oldSection = getSection(assoc);
IObjectMappingCategoryPO newSection = getSection(target);
IObjectMappingCategoryPO unmappedTechnicalNames =
editor.getAut().getObjMap().getUnmappedTechnicalCategory();
return unmappedTechnicalNames.equals(newSection)
|| oldSection.equals(newSection);
}
/**
*
* @param target The target category into which the Component Names would
* be moved.
* @param editor The editor in which the move operation would occur.
* @return <code>true</code> if the arguments represent a valid move
* operation. Otherwise <code>false</code>.
*/
public static boolean canMoveCompNames(
IObjectMappingCategoryPO target,
ObjectMappingMultiPageEditor editor) {
return getSection(target).equals(
editor.getAut().getObjMap().getUnmappedLogicalCategory());
}
/**
*
* @param startCategory The category to check.
* @return the top-level category to which the given category belongs.
*/
public static IObjectMappingCategoryPO getSection(
IObjectMappingCategoryPO startCategory) {
return startCategory != null ? startCategory.getSection() : null;
}
/**
*
* @param assoc The association to check.
* @return the top-level category to which the given association belongs.
*/
public static IObjectMappingCategoryPO getSection(
IObjectMappingAssoziationPO assoc) {
return assoc != null ? assoc.getSection() : null;
}
/**
* Moving categories...
* @param cats the categories
* @param targ the target
* @return whether the move ws successful
*/
public static boolean moveCategories(List<IObjectMappingCategoryPO> cats,
IObjectMappingCategoryPO targ) {
List<IObjectMappingCategoryPO> topCats = new ArrayList<>();
boolean isTop;
IObjectMappingCategoryPO par;
for (IObjectMappingCategoryPO cat : cats) {
isTop = true;
par = cat;
while (isTop && par != null) {
par = par.getParent();
if (cats.contains(par)) {
isTop = false;
}
}
if (isTop) {
topCats.add(cat);
}
}
correctNames(topCats, targ);
for (IObjectMappingCategoryPO cat : topCats) {
cat.getParent().removeCategory(cat);
targ.addCategory(cat);
}
return true;
}
/**
* Checks and corrects any name collisions before moving categories
* @param cats the categories
* @param target the target category
*/
private static void correctNames(List<IObjectMappingCategoryPO> cats,
IObjectMappingCategoryPO target) {
Map<String, Integer> nameMap = new HashMap<>();
for (IObjectMappingCategoryPO cat
: target.getUnmodifiableCategoryList()) {
nameMap.put(cat.getName(), 0);
}
Integer val;
IObjectMappingCategoryPO next;
String name;
for (int i = 0; i < cats.size(); i++) {
next = cats.get(i);
name = next.getName();
val = nameMap.get(name);
if (val == null) {
continue;
}
val++;
next.setName(name + "_" + val); //$NON-NLS-1$
if (nameMap.containsKey(next.getName())) {
// unlikely, but the new name may also be used...
i--;
} else {
nameMap.put(name, val);
}
}
}
}