/******************************************************************************* * 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: ******************************************************************************/ package org.eclipse.emf.emfstore.client.ui.dialogs.merge.conflict; import java.util.ArrayList; import java.util.List; import java.util.Observable; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.emfstore.client.model.util.WorkspaceUtil; import org.eclipse.emf.emfstore.client.ui.dialogs.merge.DecisionManager; import org.eclipse.emf.emfstore.client.ui.dialogs.merge.conflict.ConflictOption.OptionType; import org.eclipse.emf.emfstore.client.ui.dialogs.merge.util.DecisionUtil; import org.eclipse.emf.emfstore.server.model.versioning.operations.AbstractOperation; import org.eclipse.emf.emfstore.server.model.versioning.operations.FeatureOperation; /** * Main class representing a conflict. it offers all kind of convenience methods * and organizes the conflicts initialization. Read the constructor's * description for further implemenation details ( * {@link #Conflict(List, List, DecisionManager)}) * * @author wesendon */ public abstract class Conflict extends Observable { private DecisionManager decisionManager; private ArrayList<ConflictOption> options; private ConflictOption solution; private ConflictContext conflictContext; private ConflictDescription conflictDescription; /** * List of operations. * * @see #Conflict(List, List, DecisionManager) */ private List<AbstractOperation> leftOperations; private List<AbstractOperation> rightOperations; private boolean leftIsMy; /** * Default constructor for conflicts. Many conflicts only need one operation * for my and their side. But in order to use a suitable upper class for all * conflicts, conflicts requires a list of operations. opsA ~ myOperations, * opsB ~ theirOperations, but again, to keep it general, it's called A and * B. These fields are protected so the implementing Conflict should create * it's own getter method. * * @param leftOperations * first list of operations (often: myOperations) * @param rightOperations * second list of operations (often: theirOperations) * @param decisionManager * decision manager */ public Conflict(List<AbstractOperation> leftOperations, List<AbstractOperation> rightOperations, DecisionManager decisionManager) { this(leftOperations, rightOperations, decisionManager, true, true); } /** * Determines whether left operations are my. * * @return boolean */ public boolean isLeftMy() { return leftIsMy; } /** * 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 decisionManager * decision manager * @param leftIsMy * left operations are my changes * @param init * allows to deactivate initialization, has to be done manually * otherwise. */ public Conflict(List<AbstractOperation> leftOperations, List<AbstractOperation> rightOperations, DecisionManager decisionManager, boolean leftIsMy, boolean init) { this.leftIsMy = leftIsMy; this.leftOperations = leftOperations; this.rightOperations = rightOperations; this.decisionManager = decisionManager; if (init) { init(); } } /** * Initiates the conflict. */ protected void init() { conflictContext = initConflictContext(); conflictDescription = initConflictDescription(); options = new ArrayList<ConflictOption>(); initConflictOptions(options); initAdditionalConflictOptions(options); } private void initAdditionalConflictOptions( ArrayList<ConflictOption> options2) { if (!allowOtherOptions()) { return; } IConfigurationElement[] config = Platform .getExtensionRegistry() .getConfigurationElementsFor( "org.eclipse.emf.emfstore.client.ui.merge.customoption"); for (IConfigurationElement e : config) { try { Object object = e.createExecutableExtension("class"); if (object instanceof CustomConflictOptionFactory) { CustomConflictOptionFactory factory = (CustomConflictOptionFactory) object; if (factory.isApplicableConflict(this)) { CustomConflictOption customConflictOption = factory .createCustomConflictOption(this); if (customConflictOption != null) { options.add(customConflictOption); } } } } catch (CoreException e1) { WorkspaceUtil.logException( "Couldn't load merge option extension point.", e1); // fail silently } } } /** * Defines whether other option should be allowed via extension. E.g. Issue * option. * * @return true, if other options are allowed */ protected boolean allowOtherOptions() { return true; } /** * 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() { ConflictDescription description = new ConflictDescription(""); description.setImage("notset.gif"); EObject modelElement = getDecisionManager().getModelElement( getMyOperation().getModelElementId()); if (modelElement != null) { description.add("modelelement", modelElement); } if (getMyOperation() instanceof FeatureOperation) { description.add("feature", ((FeatureOperation) getMyOperation()).getFeatureName()); } 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 (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 list of ops. */ public List<AbstractOperation> getRejectedTheirs() { if (!isResolved()) { throw new IllegalStateException( "Can't call this method, unless conflict is resolved."); } if (solution.getType() == OptionType.TheirOperation) { return new ArrayList<AbstractOperation>(); } else { for (ConflictOption options : getOptions()) { if (options.getType() == OptionType.TheirOperation) { return options.getOperations(); } } } throw new IllegalStateException("No TheirOperations found."); // return new ArrayList<AbstractOperation>(); } /** * This method is used by {@link DecisionManager} in order to create the * resulting operations. * * @return list of ops */ public List<AbstractOperation> getAcceptedMine() { if (!isResolved()) { throw new IllegalStateException( "Can't call this method, unless conflict is resolved."); } if (solution.getType() == OptionType.TheirOperation) { return new ArrayList<AbstractOperation>(); } else { 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 */ protected List<AbstractOperation> getMyOperations() { return ((leftIsMy) ? leftOperations : rightOperations); } /** * Get their operations. * * @return list of operations */ protected List<AbstractOperation> getTheirOperations() { return ((!leftIsMy) ? leftOperations : rightOperations); } /** * Get left operations. * * @return list of operations */ protected List<AbstractOperation> getLeftOperations() { return leftOperations; } /** * get right operations. * * @return list of operations */ protected List<AbstractOperation> getRightOperations() { return rightOperations; } /** * Get first left operation. * * @return operation */ protected AbstractOperation getLeftOperation() { return leftOperations.get(0); } /** * get first right operation. * * @return operation */ protected AbstractOperation getRightOperation() { return rightOperations.get(0); } /** * Get my operation. * * @return operation */ protected AbstractOperation getMyOperation() { return getMyOperations().get(0); } /** * Get their operation. * * @return operation */ protected AbstractOperation getTheirOperation() { return getTheirOperations().get(0); } /** * Get my operation and cast. * * @param <T> * cast type * @param clazz * {@link AbstractOperation} class to which will be casted * @return operation */ @SuppressWarnings("unchecked") protected <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") protected <T> T getTheirOperation(Class<T> clazz) { return (T) getTheirOperation(); } }