| /******************************************************************************* |
| * Copyright (c) 2008-2011 Chair for Applied Software Engineering, |
| * Technische Universitaet Muenchen. |
| * 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: |
| * Otto von Wesendonk - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.emf.emfstore.internal.client.model.changeTracking.merging.conflict; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Observable; |
| import java.util.Set; |
| |
| import org.apache.commons.lang.StringUtils; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.emfstore.internal.client.model.changeTracking.merging.DecisionManager; |
| import org.eclipse.emf.emfstore.internal.client.model.changeTracking.merging.conflict.ConflictOption.OptionType; |
| import org.eclipse.emf.emfstore.internal.client.model.changeTracking.merging.util.DecisionUtil; |
| import org.eclipse.emf.emfstore.internal.server.conflictDetection.ConflictBucket; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.AbstractOperation; |
| import org.eclipse.emf.emfstore.internal.server.model.versioning.operations.FeatureOperation; |
| |
| /** |
| * Main class representing a conflict. It offers all kind of convenience methods |
| * and organizes the conflicts initialization. |
| * |
| * @author wesendon |
| */ |
| public abstract class VisualConflict extends Observable { |
| |
| private final DecisionManager decisionManager; |
| private ArrayList<ConflictOption> options; |
| private ConflictOption solution; |
| private ConflictContext conflictContext; |
| private ConflictDescription conflictDescription; |
| |
| /** |
| * List of operations. |
| * |
| * @see #Conflict(List, List, DecisionManager) |
| */ |
| private final Set<AbstractOperation> leftOperations; |
| private final Set<AbstractOperation> rightOperations; |
| private final AbstractOperation rightOperation; |
| private final AbstractOperation leftOperation; |
| private boolean leftIsMy; |
| private ConflictBucket conflictBucket; |
| |
| /** |
| * Construct conflict from given specific left and right operation. |
| * |
| * @param conflictBucket the underlying conflict |
| * @param leftOperation the left operation |
| * @param rightOperation the right operation |
| * @param decisionManager the decision manager |
| * @param leftIsMy true if left operation is my operation |
| * @param init true if conflict should be initialized on construction |
| */ |
| public VisualConflict(ConflictBucket conflictBucket, AbstractOperation leftOperation, |
| AbstractOperation rightOperation, |
| DecisionManager decisionManager, |
| boolean leftIsMy, boolean init) { |
| this(set(leftOperation), set(rightOperation), leftOperation, rightOperation, decisionManager, leftIsMy, init); |
| this.conflictBucket = conflictBucket; |
| } |
| |
| /** |
| * Construct conflict from given specific left and right operation. |
| * |
| * @param conflictBucket the underlying conflict |
| * @param decisionManager the decision manager |
| * @param leftIsMy true if left operation is my operation |
| * @param init true if conflict should be initialized on construction |
| */ |
| public VisualConflict(ConflictBucket conflictBucket, DecisionManager decisionManager, boolean leftIsMy, |
| boolean init) { |
| this(leftIsMy ? conflictBucket.getMyOperations() : conflictBucket.getTheirOperations(), |
| leftIsMy ? conflictBucket.getTheirOperations() : conflictBucket.getMyOperations(), |
| leftIsMy ? conflictBucket.getMyOperation() : conflictBucket.getTheirOperation(), |
| leftIsMy ? conflictBucket.getTheirOperation() : conflictBucket.getMyOperation(), |
| decisionManager, leftIsMy, init); |
| this.conflictBucket = conflictBucket; |
| } |
| |
| /** |
| * @return the conflictBucket |
| */ |
| protected ConflictBucket getConflictBucket() { |
| return conflictBucket; |
| } |
| |
| /** |
| * Constructor. |
| * |
| * @param conflictBucket |
| * the underlying conflict |
| * @param decisionManager |
| * the decision manager |
| */ |
| public VisualConflict(ConflictBucket conflictBucket, DecisionManager decisionManager) { |
| this(conflictBucket, decisionManager, true, true); |
| } |
| |
| /** |
| * Additional constructor, which allows deactivating initialization. |
| * |
| * @see #Conflict(List, List, DecisionManager) |
| * @param leftOperations |
| * first list of operations (often: myOperations) |
| * @param rightOperations |
| * second list of operations (often: theirOperations) |
| * @param leftOperation an operation representing all left operations |
| * @param rightOperation an operation representing all right operations |
| * @param decisionManager |
| * decision manager |
| * @param leftIsMy |
| * left operations are my changes |
| * @param init |
| * allows to deactivate initialization, has to be done manually |
| * otherwise. |
| */ |
| private VisualConflict(Set<AbstractOperation> leftOperations, Set<AbstractOperation> rightOperations, |
| AbstractOperation leftOperation, AbstractOperation rightOperation, DecisionManager decisionManager, |
| boolean leftIsMy, boolean init) { |
| this.leftOperation = leftOperation; |
| this.rightOperation = rightOperation; |
| this.leftIsMy = leftIsMy; |
| this.leftOperations = leftOperations; |
| this.rightOperations = rightOperations; |
| this.decisionManager = decisionManager; |
| |
| if (init) { |
| init(); |
| } |
| } |
| |
| private static <T> Set<T> set(T object) { |
| final Set<T> set = new LinkedHashSet<T>(); |
| set.add(object); |
| return set; |
| } |
| |
| /** |
| * Determines whether left operations are my. |
| * |
| * @return boolean |
| */ |
| public boolean isLeftMy() { |
| return leftIsMy; |
| } |
| |
| /** |
| * Initiates the conflict. |
| */ |
| protected void init() { |
| conflictContext = initConflictContext(); |
| conflictDescription = initConflictDescription(); |
| options = new ArrayList<ConflictOption>(); |
| initConflictOptions(options); |
| } |
| |
| /** |
| * Is called in order to init the options. |
| * |
| * @param options |
| * list of options |
| */ |
| protected abstract void initConflictOptions(List<ConflictOption> options); |
| |
| /** |
| * Init conflict description. |
| * |
| * @param description |
| * pre initialized description |
| * @return description |
| */ |
| protected abstract ConflictDescription initConflictDescription(ConflictDescription description); |
| |
| private ConflictDescription initConflictDescription() { |
| final ConflictDescription description = new ConflictDescription(StringUtils.EMPTY); |
| description.setImage("notset.gif"); //$NON-NLS-1$ |
| final EObject modelElement = getDecisionManager().getModelElement(getMyOperation().getModelElementId()); |
| if (modelElement != null) { |
| description.add("modelelement", modelElement); //$NON-NLS-1$ |
| } |
| if (getMyOperation() instanceof FeatureOperation) { |
| description.add("feature", ((FeatureOperation) getMyOperation()).getFeatureName()); //$NON-NLS-1$ |
| } |
| description.setDecisionManager(getDecisionManager()); |
| return initConflictDescription(description); |
| } |
| |
| /** |
| * Inits the ConflictContext. |
| * |
| * @return context. |
| */ |
| protected ConflictContext initConflictContext() { |
| return new ConflictContext(getDecisionManager(), getMyOperation(), getTheirOperation()); |
| } |
| |
| /** |
| * Returns the conflict context. |
| * |
| * @return context. |
| */ |
| public ConflictContext getConflictContext() { |
| return conflictContext; |
| } |
| |
| /** |
| * Returns the conflict description. |
| * |
| * @return conflict description |
| */ |
| public ConflictDescription getConflictDescription() { |
| return conflictDescription; |
| } |
| |
| /** |
| * Returns the list of options. |
| * |
| * @return list options |
| */ |
| public List<ConflictOption> getOptions() { |
| return options; |
| } |
| |
| /** |
| * Returns whether this conflict is resolved. |
| * |
| * @return true if resolved |
| */ |
| public boolean isResolved() { |
| return solution != null; |
| } |
| |
| /** |
| * Checks whether the related options have details. |
| * |
| * @return true, if at least one got details. |
| */ |
| public boolean hasDetails() { |
| for (final ConflictOption option : getOptions()) { |
| if (option.isDetailsProvider()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Sets an options as solution for this conflict. |
| * |
| * @param conflictOption |
| * option |
| */ |
| public void setSolution(ConflictOption conflictOption) { |
| solution = conflictOption; |
| setChanged(); |
| notifyObservers(); |
| } |
| |
| /** |
| * Returns the {@link DecisionManager}. |
| * |
| * @return decisionManager |
| */ |
| public DecisionManager getDecisionManager() { |
| return decisionManager; |
| } |
| |
| /** |
| * Returns the solution. |
| * |
| * @return solution |
| */ |
| public ConflictOption getSolution() { |
| return solution; |
| } |
| |
| /** |
| * This method is used by {@link DecisionManager} in order to create the |
| * resulting operations. |
| * |
| * @return a set containing the rejected operations |
| */ |
| public Set<AbstractOperation> getRejectedTheirs() { |
| |
| if (!isResolved()) { |
| throw new IllegalStateException(Messages.VisualConflict_Conflict_Not_Resolved); |
| } |
| |
| if (solution.getType() == OptionType.TheirOperation) { |
| return Collections.emptySet(); |
| } |
| |
| for (final ConflictOption options : getOptions()) { |
| if (options.getType() == OptionType.TheirOperation) { |
| return options.getOperations(); |
| } |
| } |
| |
| throw new IllegalStateException(Messages.VisualConflict_No_TheirOps); |
| } |
| |
| /** |
| * This method is used by {@link DecisionManager} in order to create the |
| * resulting operations. |
| * |
| * @return set containing the accepted operations |
| */ |
| public Set<AbstractOperation> getAcceptedMine() { |
| |
| if (!isResolved()) { |
| throw new IllegalStateException(Messages.VisualConflict_Conflict_Not_Resolved); |
| } |
| |
| if (solution.getType() == OptionType.TheirOperation) { |
| return Collections.emptySet(); |
| } |
| |
| return solution.getOperations(); |
| } |
| |
| /** |
| * Get an option by its type. |
| * |
| * @param type |
| * type |
| * @return option or null |
| */ |
| public ConflictOption getOptionOfType(OptionType type) { |
| return DecisionUtil.getConflictOptionByType(getOptions(), type); |
| } |
| |
| /** |
| * Get my operations. |
| * |
| * @return list of operations |
| */ |
| public Set<AbstractOperation> getMyOperations() { |
| return leftIsMy ? leftOperations : rightOperations; |
| } |
| |
| /** |
| * Get their operations. |
| * |
| * @return list of operations |
| */ |
| public Set<AbstractOperation> getTheirOperations() { |
| return !leftIsMy ? leftOperations : rightOperations; |
| } |
| |
| /** |
| * Get left operations. |
| * |
| * @return list of operations |
| */ |
| public Set<AbstractOperation> getLeftOperations() { |
| return leftOperations; |
| } |
| |
| /** |
| * get right operations. |
| * |
| * @return list of operations |
| */ |
| public Set<AbstractOperation> getRightOperations() { |
| return rightOperations; |
| } |
| |
| /** |
| * Get first left operation. |
| * |
| * @return operation |
| */ |
| public AbstractOperation getLeftOperation() { |
| return leftOperation; |
| } |
| |
| /** |
| * get first right operation. |
| * |
| * @return operation |
| */ |
| public AbstractOperation getRightOperation() { |
| return rightOperation; |
| } |
| |
| /** |
| * Get my operation. |
| * |
| * @return operation |
| */ |
| public AbstractOperation getMyOperation() { |
| return leftIsMy ? leftOperation : rightOperation; |
| } |
| |
| /** |
| * Get their operation. |
| * |
| * @return operation |
| */ |
| public AbstractOperation getTheirOperation() { |
| return !leftIsMy ? leftOperation : rightOperation; |
| } |
| |
| /** |
| * Get my operation and cast. |
| * |
| * @param <T> |
| * cast type |
| * @param clazz |
| * {@link AbstractOperation} class to which will be casted |
| * @return operation |
| */ |
| @SuppressWarnings("unchecked") |
| public <T> T getMyOperation(Class<T> clazz) { |
| return (T) getMyOperation(); |
| } |
| |
| /** |
| * Get their operation and cast. |
| * |
| * @param <T> |
| * cast type |
| * @param clazz |
| * {@link AbstractOperation} class to which will be casted |
| * @return operation |
| */ |
| @SuppressWarnings("unchecked") |
| public <T> T getTheirOperation(Class<T> clazz) { |
| return (T) getTheirOperation(); |
| } |
| |
| /** |
| * Resolves this conflict. |
| */ |
| public void resolve() { |
| conflictBucket.resolveConflict(getAcceptedMine(), getRejectedTheirs()); |
| } |
| |
| /** |
| * Whether the left operations is mine operation. |
| * |
| * @param leftIsMy |
| * should be {@code true}, if the left operation belongs to me, {@code false} otherwise |
| */ |
| public void setLeftIsMy(boolean leftIsMy) { |
| this.leftIsMy = leftIsMy; |
| } |
| } |