//------------------------------------------------------------------------------ // Copyright (c) 2005, 2006 IBM Corporation and others. // 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: // IBM Corporation - initial implementation //------------------------------------------------------------------------------ package org.eclipse.epf.library.edit.process.command; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.eclipse.emf.common.CommonPlugin; import org.eclipse.emf.common.command.BasicCommandStack; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.command.CompoundCommand; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.util.AbstractTreeIterator; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.edit.command.RemoveCommand; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.epf.library.edit.IConfigurator; import org.eclipse.epf.library.edit.LibraryEditResources; import org.eclipse.epf.library.edit.Providers; import org.eclipse.epf.library.edit.TngAdapterFactory; import org.eclipse.epf.library.edit.command.BatchCommand; import org.eclipse.epf.library.edit.command.IResourceAwareCommand; import org.eclipse.epf.library.edit.ui.UserInteractionHelper; import org.eclipse.epf.library.edit.util.Messenger; import org.eclipse.epf.library.edit.util.ProcessUtil; import org.eclipse.epf.library.edit.util.TngUtil; import org.eclipse.epf.uma.Activity; import org.eclipse.epf.uma.BreakdownElement; import org.eclipse.epf.uma.Deliverable; import org.eclipse.epf.uma.Descriptor; import org.eclipse.epf.uma.MethodConfiguration; import org.eclipse.epf.uma.MethodElement; import org.eclipse.epf.uma.Process; import org.eclipse.epf.uma.Role; import org.eclipse.epf.uma.RoleDescriptor; import org.eclipse.epf.uma.Task; import org.eclipse.epf.uma.TaskDescriptor; import org.eclipse.epf.uma.TeamProfile; import org.eclipse.epf.uma.VariabilityElement; import org.eclipse.epf.uma.WorkProduct; import org.eclipse.epf.uma.WorkProductDescriptor; /** * Command for one-way synchronization from method to process. * * @author Phong Nguyen Le - Nov 22, 2005 * @since 1.0 */ public class SynchronizeCommand extends CompoundCommand implements IResourceAwareCommand { private Collection elements; private IConfigurator configurator; protected boolean aborted; private ArrayList deleteList; protected boolean preExecSuccessful; protected List deleteCommandList; protected boolean successful; private MethodConfiguration config; private Set synchFeatures; private DeleteUnusedDescriptorsCommand deleteUnusedDescriptorsCommand; private Collection activities; private boolean showSuccessfulMsg = true; private boolean intialized; private BatchCommand batchCommand = new BatchCommand(false); private Map<VariabilityElement, VariabilityElement> replacerToBaseMap = new HashMap<VariabilityElement, VariabilityElement>(); // private Object UIContext; /** * Constructs a SynchronizeCommand that use the process default * configuration to synchronize all the synchronizable features * * @param elements * a list of BreakdownElement objects */ public SynchronizeCommand(String label, Collection elements) { super(label); this.elements = elements; } /** * Constructs a SynchronizeCommand that use the given configuration * and list of synchronizable features to synchronize * * @param elements * @param config the configuration * @param synchFeatures the synchronizable features */ public SynchronizeCommand(Collection elements, MethodConfiguration config, Set synchFeatures, boolean showSuccessfulMsg) { super(LibraryEditResources.AutoSynchronizeCommand_label); this.elements = elements; this.config = config; this.synchFeatures = synchFeatures; this.showSuccessfulMsg = showSuccessfulMsg; } public void setMethodConfiguration(MethodConfiguration config) { this.config = config; if(configurator != null) { configurator.setMethodConfiguration(config); } } public void setSynchronizationFeatures(Set synchFeatures) { this.synchFeatures = synchFeatures; } private boolean doInitialize() { commandList.clear(); if (elements == null || elements.isEmpty()) { return false; } deleteCommandList = new ArrayList(); deleteList = new ArrayList(); activities = new ArrayList(); for (Iterator iter = elements.iterator(); iter.hasNext();) { Object element = (Object) iter.next(); addToDeleteList(element, deleteList); if(element instanceof Activity) { activities.add(element); } } elements.removeAll(deleteList); for (Iterator<?> iter = elements.iterator(); iter.hasNext();) { Object object = iter.next(); if (object instanceof Descriptor) { if(!deleteList.contains(object)) { Descriptor descriptor = (Descriptor) object; if (descriptor.getIsSynchronizedWithSource().booleanValue()) { if(descriptor.getSuperActivities() == null) { // descriptor is used by TeamProfile to represent a role or used by a deliverable descriptor to // represent a deliverable part // if(descriptor instanceof WorkProductDescriptor && ((WorkProductDescriptor)descriptor).getWorkProduct() instanceof Deliverable) { append(new SynchronizeDeliverableDescriptorCommand((WorkProductDescriptor) descriptor, synchFeatures, config)); } else { append(new BasicSynchronizeDescriptorCommand(descriptor, synchFeatures, config)); } } else { Activity act = descriptor.getSuperActivities(); if (object instanceof TaskDescriptor) { Task task = ((TaskDescriptor) object).getTask(); if (task != null) { if(replacerToBaseMap.containsKey(task)) { task = (Task) replacerToBaseMap.get(task); } append(new WBSDropCommand(act, Collections .singletonList(task), Collections .singletonList((TaskDescriptor)object), config, synchFeatures)); } } else if (object instanceof RoleDescriptor) { Role role = ((RoleDescriptor) object).getRole(); if (role != null) { if(replacerToBaseMap.containsKey(role)) { role = (Role) replacerToBaseMap.get(role); } append(new OBSDropCommand(act, Collections .singletonList(role), config, synchFeatures, configurator)); } } else if (object instanceof WorkProductDescriptor) { WorkProduct wp = ((WorkProductDescriptor) object) .getWorkProduct(); if (wp != null) { if(replacerToBaseMap.containsKey(wp)) { wp = (WorkProduct) replacerToBaseMap.get(wp); } append(new PBSDropCommand(act, Collections .singletonList(wp), config, synchFeatures, configurator)); } } } } else { appendRemoveDuplicateGuidanceRefCommand(descriptor); } } } else if (object instanceof Activity) { appendCommands((Activity) object); } else if (object instanceof TeamProfile) { appendCommands((TeamProfile)object); } } return !deleteList.isEmpty() || !commandList.isEmpty() || !activities.isEmpty(); } public boolean initilize() { boolean b = isPrepared; try { // since Eclipse 3.3, CompoundCommand.append() throws IllegalStateException if isPrepared is already set // but this method is called after isPrepared is set in canExecute() and it makes many calls to append() // work-around is to unset isPrepared at beginning of this method and set it back to old value at method end. // isPrepared = false; return intialized = doInitialize(); } finally { isPrepared = b; } } /** * @return the intialized */ public boolean isIntialized() { return intialized; } /** * @param activity */ private void appendCommands(Activity activity) { List<Task> tasks = new ArrayList<Task>(); List<TaskDescriptor> tds = new ArrayList<TaskDescriptor>(); List<Role> roles = new ArrayList<Role>(); List<WorkProduct> workProducts = new ArrayList<WorkProduct>(); List<Activity> activities = new ArrayList<Activity>(); for (Iterator<?> iter = activity.getBreakdownElements().iterator(); iter .hasNext();) { Object element = iter.next(); if (element instanceof Descriptor) { if(!deleteList.contains(element)) { Descriptor descriptor = ((Descriptor) element); if (descriptor.getIsSynchronizedWithSource() .booleanValue()) { if (element instanceof TaskDescriptor) { Task task = ((TaskDescriptor) element).getTask(); if (task != null) { if(replacerToBaseMap.containsKey(task)) { task = (Task) replacerToBaseMap.get(task); } tasks.add(task); tds.add((TaskDescriptor) element); } } else if (element instanceof RoleDescriptor) { Role role = ((RoleDescriptor) element).getRole(); if (role != null) { if(replacerToBaseMap.containsKey(role)) { role = (Role) replacerToBaseMap.get(role); } roles.add(role); } } else if (element instanceof WorkProductDescriptor) { WorkProduct wp = ((WorkProductDescriptor) element) .getWorkProduct(); if (wp != null) { if(replacerToBaseMap.containsKey(wp)) { wp = (WorkProduct) replacerToBaseMap.get(wp); } workProducts.add(wp); } } } else { appendRemoveDuplicateGuidanceRefCommand(descriptor); } } } else if (element instanceof Activity) { activities.add((Activity) element); } else if (element instanceof TeamProfile) { appendCommands((TeamProfile)element); } } if (!tasks.isEmpty()) { append(new WBSDropCommand(activity, tasks, tds, config, synchFeatures)); } if (!roles.isEmpty()) { append(new OBSDropCommand(activity, roles, config, synchFeatures, configurator)); } if (!workProducts.isEmpty()) { append(new PBSDropCommand(activity, workProducts, config, synchFeatures, configurator)); } for (Iterator<Activity> iter = activities.iterator(); iter.hasNext();) { appendCommands(iter.next()); } } private void appendRemoveDuplicateGuidanceRefCommand(Descriptor descriptor) { append(new RemoveDuplicateReferenceCommand(descriptor, ProcessCommandUtil.CONTENT_ELEMENT_GUIDANCE_REFERENCES, config)); } private void appendCommands(TeamProfile team) { Iterator iter = new AbstractTreeIterator(team, false) { /** * */ private static final long serialVersionUID = 1L; protected Iterator getChildren(Object object) { if(object instanceof TeamProfile) { TeamProfile team = ((TeamProfile)object); List children = new ArrayList(team.getSubTeam()); children.addAll(team.getTeamRoles()); return children.iterator(); } else { return Collections.EMPTY_LIST.iterator(); } } }; while(iter.hasNext()) { Object obj = iter.next(); // synch only own role descriptor of team profile // if(obj instanceof RoleDescriptor && ((RoleDescriptor)obj).getSuperActivities() == null) { append(new BasicSynchronizeDescriptorCommand((Descriptor) obj, synchFeatures, config)); } } } /* * (non-Javadoc) * * @see com.ibm.library.edit.command.IResourceAwareCommand#getModifiedResources() */ public Collection getModifiedResources() { HashSet modifiedResources = new HashSet(); for (Iterator iter = commandList.iterator(); iter.hasNext();) { Object cmd = iter.next(); if(cmd instanceof IResourceAwareCommand) { modifiedResources.addAll(((IResourceAwareCommand)cmd).getModifiedResources()); } } return modifiedResources; } // public void setUIContext(Object UIContext) { // this.UIContext = UIContext; // } /* * (non-Javadoc) * * @see org.eclipse.emf.common.command.CompoundCommand#execute() */ public void execute() { // delete all descriptors whose linked element are no longer in the // configuration // if (!deleteList.isEmpty()) { Command cmd = delete(deleteList); if (cmd != null) { deleteCommandList.add(cmd); } } batchCommand.execute(); if (!aborted) { // IRunnableWithProgress runnable = new IRunnableWithProgress() { // // public void run(IProgressMonitor monitor) // throws InvocationTargetException, InterruptedException { // preExecSuccessful = preExecute(); // } // // }; // // UserInteractionHelper.runWithProgress(runnable, false, null); // // if (preExecSuccessful) { // try { // BusyIndicator.showWhile(Display.getCurrent(), new Runnable() { // // public void run() { // superRedo(); // successful = true; // } // // }); // } // catch(RuntimeException e) { // LibraryEditPlugin.getDefault().getMsgDialog().displayError(getLabel(), e.toString()); // } // } Runnable runnable = new Runnable() { public void run() { preExecSuccessful = preExecute(); if(preExecute()) { superRedo(); successful = true; } } }; UserInteractionHelper.runInUI(runnable, getLabel()); if (successful) { if(!activities.isEmpty()) { if(deleteUnusedDescriptorsCommand == null) { deleteUnusedDescriptorsCommand = new DeleteUnusedDescriptorsCommand(elements, true, deleteList) { protected Command delete(List elements) { return SynchronizeCommand.this.delete(elements); } }; deleteCommandList.add(deleteUnusedDescriptorsCommand); } deleteUnusedDescriptorsCommand.execute(); } if (showSuccessfulMsg) { if(!replacerToBaseMap.isEmpty()) { refreshViewer(); } Messenger.INSTANCE.showInfo( LibraryEditResources.SynchronizeCompleteDialog_Title, LibraryEditResources.AutoSynchronizeCommand_sucessful); } } } } private void refreshViewer() { if(elements.isEmpty()) return; Process proc = TngUtil.getOwningProcess(elements.iterator().next()); if(proc != null) { for (int i = 0; i < TngAdapterFactory.processAdapterFactories.length; i++) { AdapterFactory adapterFactory = TngAdapterFactory.processAdapterFactories[i]; ProcessUtil.refreshViewer(adapterFactory, proc); } } } private void superRedo() { super.redo(); } /* * (non-Javadoc) * * @see org.eclipse.emf.common.command.CompoundCommand#redo() */ public void redo() { execute(); } public boolean isSucessful() { return successful; } protected boolean preExecute() { for (ListIterator commands = commandList.listIterator(); commands .hasNext();) { Object command = commands.next(); if(command instanceof BSDropCommand && !((BSDropCommand)command).preExecute()) { return false; } // be polite to other threads (no effect on some platforms) // Thread.yield(); } return true; } /** * Deletes the specified elements * @param elements * @return the executed command that deleted the given elements */ protected Command delete(List elements) { EditingDomain domain = new AdapterFactoryEditingDomain( TngAdapterFactory.INSTANCE.getProcessComposedAdapterFactory(), new BasicCommandStack()); Command cmd = new ProcessElementDeleteCommand(RemoveCommand.create(domain, elements), elements); cmd.execute(); return cmd; } private IConfigurator getConfigurator() { if (configurator == null) { MethodConfiguration config = this.config; if(config == null) { Process proc = TngUtil.getOwningProcess((BreakdownElement) elements .iterator().next()); config = proc.getDefaultContext(); } configurator = Providers.getConfiguratorFactory() .createConfigurator(config); } return configurator; } /** * @param element * @param deleteList */ private void addToDeleteList(Object element, List deleteList) { if (element instanceof Descriptor) { if (!getConfigurator().accept(element)) { MethodElement linkedElement = ProcessUtil.getAssociatedElement((Descriptor) element); if(linkedElement instanceof VariabilityElement && TngUtil.isReplacer((VariabilityElement) linkedElement)) { // if the linked element of the descriptor is a replacer, delete the descriptor in this synchronization // only if the base of linked element is not in the configuration // VariabilityElement base = ((VariabilityElement)linkedElement).getVariabilityBasedOnElement(); while(base != null && TngUtil.isContributorOrReplacer(base)) { base = base.getVariabilityBasedOnElement(); } if(base != null) { if(!getConfigurator().accept(base)) { deleteList.add(element); } else { batchCommand.addFeatureValue((EObject) element, ProcessUtil.getLinkReference((Descriptor) element), base); replacerToBaseMap.put((VariabilityElement) linkedElement, base); } } } else { deleteList.add(element); } } } else if (element instanceof Activity) { for (Iterator iter = ((Activity) element).getBreakdownElements() .iterator(); iter.hasNext();) { addToDeleteList(iter.next(), deleteList); } } } private void superUndo() { super.undo(); } /* * (non-Javadoc) * * @see org.eclipse.emf.common.command.CompoundCommand#undo() */ public void undo() { UserInteractionHelper.getUIHelper().runWithBusyIndicator(new Runnable() { public void run() { if (!deleteCommandList.isEmpty()) { for (ListIterator commands = deleteCommandList .listIterator(deleteCommandList.size()); commands .hasPrevious();) { try { Command command = (Command) commands.previous(); command.undo(); } catch (RuntimeException exception) { // Skip over the command that threw the exception. // commands.next(); try { // Iterate forward over the undone commands to // redo them. // while (commands.hasNext()) { Command command = (Command) commands.next(); command.redo(); } } catch (RuntimeException nestedException) { CommonPlugin.INSTANCE .log(new WrappedException( CommonPlugin.INSTANCE .getString("_UI_IgnoreException_exception"), nestedException).fillInStackTrace()); //$NON-NLS-1$ } throw exception; } } } superUndo(); batchCommand.undo(); } }); } /* * (non-Javadoc) * * @see org.eclipse.emf.common.command.CompoundCommand#prepare() */ protected boolean prepare() { return true; } /* (non-Javadoc) * @see org.eclipse.emf.common.command.CompoundCommand#dispose() */ public void dispose() { if(activities != null) { activities.clear(); } if(deleteCommandList != null) { for (Iterator iter = deleteCommandList.iterator(); iter.hasNext();) { Command cmd = (Command) iter.next(); cmd.dispose(); } } if(deleteList != null) { deleteList.clear(); } super.dispose(); } }