1 /*******************************************************************************
2  *  Copyright (c) 2000, 2017 IBM Corporation and others.
3  *
4  *  This program and the accompanying materials
5  *  are made available under the terms of the Eclipse Public License 2.0
6  *  which accompanies this distribution, and is available at
7  *  https://www.eclipse.org/legal/epl-2.0/
8  *
9  *  SPDX-License-Identifier: EPL-2.0
10  *
11  *  Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.team.internal.ui.synchronize.actions;
15 
16 import java.lang.reflect.InvocationTargetException;
17 import java.util.Iterator;
18 
19 import org.eclipse.compare.CompareEditorInput;
20 import org.eclipse.compare.CompareUI;
21 import org.eclipse.compare.structuremergeviewer.ICompareInput;
22 import org.eclipse.core.resources.IResource;
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.jface.action.Action;
26 import org.eclipse.jface.util.OpenStrategy;
27 import org.eclipse.jface.viewers.ISelection;
28 import org.eclipse.jface.viewers.IStructuredSelection;
29 import org.eclipse.team.core.synchronize.SyncInfo;
30 import org.eclipse.team.internal.ui.Utils;
31 import org.eclipse.team.internal.ui.mapping.ModelCompareEditorInput;
32 import org.eclipse.team.internal.ui.synchronize.SyncInfoModelElement;
33 import org.eclipse.team.internal.ui.synchronize.patch.ApplyPatchModelCompareEditorInput;
34 import org.eclipse.team.internal.ui.synchronize.patch.ApplyPatchSubscriberMergeContext;
35 import org.eclipse.team.ui.mapping.ISynchronizationCompareInput;
36 import org.eclipse.team.ui.mapping.ITeamContentProviderManager;
37 import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
38 import org.eclipse.team.ui.synchronize.ISynchronizePageSite;
39 import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
40 import org.eclipse.team.ui.synchronize.ModelSynchronizeParticipant;
41 import org.eclipse.team.ui.synchronize.SyncInfoCompareInput;
42 import org.eclipse.ui.IEditorInput;
43 import org.eclipse.ui.IEditorPart;
44 import org.eclipse.ui.IEditorReference;
45 import org.eclipse.ui.IReusableEditor;
46 import org.eclipse.ui.IWorkbenchPage;
47 import org.eclipse.ui.IWorkbenchPartSite;
48 import org.eclipse.ui.IWorkbenchWindow;
49 import org.eclipse.ui.PlatformUI;
50 
51 /**
52  * Action to open a compare editor from a SyncInfo object.
53  *
54  * @see SyncInfoCompareInput
55  * @since 3.0
56  */
57 public class OpenInCompareAction extends Action {
58 
59 	private final ISynchronizePageConfiguration configuration;
60 
OpenInCompareAction(ISynchronizePageConfiguration configuration)61 	public OpenInCompareAction(ISynchronizePageConfiguration configuration) {
62 		this.configuration = configuration;
63 		Utils.initAction(this, "action.openInCompareEditor."); //$NON-NLS-1$
64 	}
65 
66 	@Override
run()67 	public void run() {
68 		ISelection selection = configuration.getSite().getSelectionProvider().getSelection();
69 		if(selection instanceof IStructuredSelection) {
70 			if (!isOkToRun(selection))
71 				return;
72 
73 			boolean reuseEditorIfPossible = ((IStructuredSelection) selection).size()==1;
74 			for (Iterator iterator = ((IStructuredSelection) selection).iterator(); iterator.hasNext();) {
75 				Object obj = iterator.next();
76 				if (obj instanceof SyncInfoModelElement) {
77 					SyncInfo info = ((SyncInfoModelElement) obj).getSyncInfo();
78 					if (info != null) {
79 						// Use the open strategy to decide if the editor or the sync view should have focus
80 						openCompareEditorOnSyncInfo(configuration, info, !OpenStrategy.activateOnOpen(), reuseEditorIfPossible);
81 					}
82 				} else if (obj != null){
83 					openCompareEditor(configuration, obj, !OpenStrategy.activateOnOpen(), reuseEditorIfPossible);
84 				}
85 			}
86 		}
87 	}
88 
isOkToRun(ISelection selection)89 	private boolean isOkToRun(ISelection selection) {
90 		// do not open Compare Editor unless all elements have input
91 		Object[] elements = ((IStructuredSelection) selection).toArray();
92 		ISynchronizeParticipant participant = configuration
93 				.getParticipant();
94 		// model synchronize
95 		if (participant instanceof ModelSynchronizeParticipant) {
96 			ModelSynchronizeParticipant msp = (ModelSynchronizeParticipant) participant;
97 			for (Object element : elements) {
98 				// TODO: This is inefficient
99 				if (!msp.hasCompareInputFor(element)) {
100 					return false;
101 				}
102 			}
103 		} else {
104 			// all files
105 			IResource resources[] = Utils.getResources(elements);
106 			for (IResource resource : resources) {
107 				if (resource.getType() != IResource.FILE) {
108 					// Only supported if all the items are files.
109 					return false;
110 				}
111 			}
112 		}
113 		return true;
114 	}
115 
openCompareEditor(ISynchronizePageConfiguration configuration, Object object, boolean keepFocus, boolean reuseEditorIfPossible)116 	public static IEditorInput openCompareEditor(ISynchronizePageConfiguration configuration, Object object, boolean keepFocus, boolean reuseEditorIfPossible) {
117 		Assert.isNotNull(object);
118 		Assert.isNotNull(configuration);
119 		ISynchronizeParticipant participant = configuration.getParticipant();
120 		ISynchronizePageSite site = configuration.getSite();
121 		if (object instanceof SyncInfoModelElement) {
122 			SyncInfo info = ((SyncInfoModelElement) object).getSyncInfo();
123 			if (info != null)
124 				return openCompareEditorOnSyncInfo(configuration, info, keepFocus, reuseEditorIfPossible);
125 		}
126 		if (participant instanceof ModelSynchronizeParticipant) {
127 			ModelSynchronizeParticipant msp = (ModelSynchronizeParticipant) participant;
128 			ICompareInput input = msp.asCompareInput(object);
129 			IWorkbenchPage workbenchPage = getWorkbenchPage(site);
130 			if (input != null && workbenchPage != null && isOkToOpen(site, participant, input)) {
131 				if (isApplyPatchModelPresent(configuration))
132 					return openCompareEditor(workbenchPage, new ApplyPatchModelCompareEditorInput(msp, input, workbenchPage, configuration), keepFocus, site, reuseEditorIfPossible);
133 				else
134 					return openCompareEditor(workbenchPage, new ModelCompareEditorInput(msp, input, workbenchPage, configuration), keepFocus, site, reuseEditorIfPossible);
135 			}
136 		}
137 		return null;
138 	}
139 
isApplyPatchModelPresent( ISynchronizePageConfiguration configuration)140 	private static boolean isApplyPatchModelPresent(
141 			ISynchronizePageConfiguration configuration) {
142 		Object object = configuration.getProperty(ITeamContentProviderManager.P_SYNCHRONIZATION_CONTEXT);
143 		return object instanceof ApplyPatchSubscriberMergeContext;
144 	}
145 
isOkToOpen(final ISynchronizePageSite site, final ISynchronizeParticipant participant, final ICompareInput input)146 	private static boolean isOkToOpen(final ISynchronizePageSite site, final ISynchronizeParticipant participant, final ICompareInput input) {
147 		if (participant instanceof ModelSynchronizeParticipant && input instanceof ISynchronizationCompareInput) {
148 			final ModelSynchronizeParticipant msp = (ModelSynchronizeParticipant) participant;
149 			final boolean[] result = new boolean[] { false };
150 			try {
151 				PlatformUI.getWorkbench().getProgressService().run(true, true, monitor -> {
152 					try {
153 						result[0] = msp.checkForBufferChange(site.getShell(), (ISynchronizationCompareInput) input,
154 								true, monitor);
155 					} catch (CoreException e) {
156 						throw new InvocationTargetException(e);
157 					}
158 				});
159 			} catch (InvocationTargetException e) {
160 				Utils.handleError(site.getShell(), e, null, null);
161 			} catch (InterruptedException e) {
162 				return false;
163 			}
164 			return result[0];
165 		}
166 		return true;
167 	}
168 
openCompareEditorOnSyncInfo(ISynchronizePageConfiguration configuration, SyncInfo info, boolean keepFocus, boolean reuseEditorIfPossible)169 	public static CompareEditorInput openCompareEditorOnSyncInfo(ISynchronizePageConfiguration configuration, SyncInfo info, boolean keepFocus, boolean reuseEditorIfPossible) {
170 		Assert.isNotNull(info);
171 		Assert.isNotNull(configuration);
172 		if(info.getLocal().getType() != IResource.FILE) return null;
173 		SyncInfoCompareInput input = new SyncInfoCompareInput(configuration, info);
174 		return openCompareEditor(getWorkbenchPage(configuration.getSite()), input, keepFocus, configuration.getSite(), reuseEditorIfPossible);
175 	}
176 
openCompareEditor(ISynchronizeParticipant participant, SyncInfo info, ISynchronizePageSite site)177 	public static CompareEditorInput openCompareEditor(ISynchronizeParticipant participant, SyncInfo info, ISynchronizePageSite site) {
178 		Assert.isNotNull(info);
179 		Assert.isNotNull(participant);
180 		if(info.getLocal().getType() != IResource.FILE) return null;
181 		SyncInfoCompareInput input = new SyncInfoCompareInput(participant, info);
182 		return openCompareEditor(getWorkbenchPage(site), input, false, site, false);
183 	}
184 
openCompareEditor( IWorkbenchPage page, CompareEditorInput input, boolean keepFocus, ISynchronizePageSite site, boolean reuseEditorIfPossible)185 	private static CompareEditorInput openCompareEditor(
186 			IWorkbenchPage page,
187 			CompareEditorInput input,
188 			boolean keepFocus,
189 			ISynchronizePageSite site,
190 			boolean reuseEditorIfPossible) {
191 		if (page == null)
192 			return null;
193 
194 		openCompareEditor(input, page, reuseEditorIfPossible);
195 		if(site != null && keepFocus) {
196 			site.setFocus();
197 		}
198 		return input;
199 	}
200 
getWorkbenchPage(ISynchronizePageSite site)201 	private static IWorkbenchPage getWorkbenchPage(ISynchronizePageSite site) {
202 		IWorkbenchPage page = null;
203 		if(site == null || site.getWorkbenchSite() == null) {
204 			IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
205 			if (window != null)
206 				page = window.getActivePage();
207 		} else {
208 			page = site.getWorkbenchSite().getPage();
209 		}
210 		return page;
211 	}
212 
openCompareEditor(CompareEditorInput input, IWorkbenchPage page)213 	public static void openCompareEditor(CompareEditorInput input, IWorkbenchPage page) {
214 		// try to reuse editors, if possible
215 		openCompareEditor(input, page, true);
216 	}
217 
openCompareEditor(CompareEditorInput input, IWorkbenchPage page, boolean reuseEditorIfPossible)218 	public static void openCompareEditor(CompareEditorInput input, IWorkbenchPage page, boolean reuseEditorIfPossible) {
219 		if (page == null || input == null)
220 			return;
221 		IEditorPart editor = Utils.findReusableCompareEditor(input, page,
222 				new Class[] { SyncInfoCompareInput.class,
223 						ModelCompareEditorInput.class });
224 		// reuse editor only for single selection
225 		if(editor != null && reuseEditorIfPossible) {
226 			IEditorInput otherInput = editor.getEditorInput();
227 			if(otherInput.equals(input)) {
228 				// simply provide focus to editor
229 				page.activate(editor);
230 			} else {
231 				// if editor is currently not open on that input either re-use existing
232 				CompareUI.reuseCompareEditor(input, (IReusableEditor)editor);
233 				page.activate(editor);
234 			}
235 		} else {
236 			CompareUI.openCompareEditorOnPage(input, page);
237 		}
238 	}
239 
240 	/**
241 	 * Returns an editor handle if a SyncInfoCompareInput compare editor is opened on
242 	 * the given IResource.
243 	 *
244 	 * @param site the view site in which to search for editors
245 	 * @param resource the resource to use to find the compare editor
246 	 * @return an editor handle if found and <code>null</code> otherwise
247 	 */
findOpenCompareEditor(IWorkbenchPartSite site, IResource resource)248 	public static IEditorPart findOpenCompareEditor(IWorkbenchPartSite site, IResource resource) {
249 		IWorkbenchPage page = site.getPage();
250 		IEditorReference[] editorRefs = page.getEditorReferences();
251 		for (IEditorReference editorRef : editorRefs) {
252 			final IEditorPart part = editorRef.getEditor(false /* don't restore editor */);
253 			if(part != null) {
254 				IEditorInput input = part.getEditorInput();
255 				if(part != null && input instanceof SyncInfoCompareInput) {
256 					SyncInfo inputInfo = ((SyncInfoCompareInput)input).getSyncInfo();
257 					if(inputInfo.getLocal().equals(resource)) {
258 						return part;
259 					}
260 				}
261 			}
262 		}
263 		return null;
264 	}
265 
266 	/**
267 	 * Returns an editor handle if a compare editor is opened on
268 	 * the given object.
269 	 * @param site the view site in which to search for editors
270 	 * @param object the object to use to find the compare editor
271 	 * @param participant
272 	 * @return an editor handle if found and <code>null</code> otherwise
273 	 */
findOpenCompareEditor(IWorkbenchPartSite site, Object object, ISynchronizeParticipant participant)274 	public static IEditorPart findOpenCompareEditor(IWorkbenchPartSite site, Object object, ISynchronizeParticipant participant) {
275 		if (object instanceof SyncInfoModelElement) {
276 			SyncInfoModelElement element = (SyncInfoModelElement) object;
277 			SyncInfo info = element.getSyncInfo();
278 			return findOpenCompareEditor(site, info.getLocal());
279 		}
280 		IWorkbenchPage page = site.getPage();
281 		IEditorReference[] editorRefs = page.getEditorReferences();
282 		for (IEditorReference editorRef : editorRefs) {
283 			final IEditorPart part = editorRef.getEditor(false /* don't restore editor */);
284 			if(part != null) {
285 				IEditorInput input = part.getEditorInput();
286 				if(input instanceof ModelCompareEditorInput) {
287 					if(((ModelCompareEditorInput)input).matches(object, participant)) {
288 						return part;
289 					}
290 				}
291 			}
292 		}
293 		return null;
294 	}
295 }
296