1 /*******************************************************************************
2 * Copyright (c) 2013, 2017 Obeo and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Obeo - initial API and implementation
10 * Michael Borkowski - bug 467191
11 * Philip Langer - bug 462884, 516576
12 * Stefan Dirix - bugs 473985, 474030
13 * Martin Fleck - bug 497066, 483798, 514767, 514415
14 * Alexandra Buzila - bug 513931
15 *******************************************************************************/
16 package org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
;
18 import static com
.google
.common
.base
.Predicates
.instanceOf
;
19 import static com
.google
.common
.collect
.Iterables
.any
;
20 import static org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
.EMFCompareStructureMergeViewerContentProvider
.CallbackType
.IN_UI_ASYNC
;
21 import static org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
.EMFCompareStructureMergeViewerContentProvider
.CallbackType
.IN_UI_SYNC
;
23 import com
.google
.common
.base
.Function
;
24 import com
.google
.common
.base
.Objects
;
25 import com
.google
.common
.base
.Predicate
;
26 import com
.google
.common
.base
.Throwables
;
27 import com
.google
.common
.collect
.Iterables
;
28 import com
.google
.common
.collect
.Iterators
;
29 import com
.google
.common
.collect
.Lists
;
30 import com
.google
.common
.collect
.Maps
;
31 import com
.google
.common
.eventbus
.Subscribe
;
33 import java
.util
.ArrayList
;
34 import java
.util
.Collection
;
35 import java
.util
.Collections
;
36 import java
.util
.EnumSet
;
37 import java
.util
.EventObject
;
38 import java
.util
.Iterator
;
39 import java
.util
.List
;
43 import org
.eclipse
.compare
.CompareUI
;
44 import org
.eclipse
.compare
.CompareViewerPane
;
45 import org
.eclipse
.compare
.CompareViewerSwitchingPane
;
46 import org
.eclipse
.compare
.ICompareInputLabelProvider
;
47 import org
.eclipse
.compare
.INavigatable
;
48 import org
.eclipse
.compare
.ITypedElement
;
49 import org
.eclipse
.compare
.ResourceNode
;
50 import org
.eclipse
.compare
.structuremergeviewer
.DiffNode
;
51 import org
.eclipse
.compare
.structuremergeviewer
.ICompareInput
;
52 import org
.eclipse
.compare
.structuremergeviewer
.ICompareInputChangeListener
;
53 import org
.eclipse
.core
.resources
.IStorage
;
54 import org
.eclipse
.core
.resources
.ResourceAttributes
;
55 import org
.eclipse
.core
.runtime
.Assert
;
56 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
57 import org
.eclipse
.core
.runtime
.IStatus
;
58 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
59 import org
.eclipse
.core
.runtime
.OperationCanceledException
;
60 import org
.eclipse
.core
.runtime
.Status
;
61 import org
.eclipse
.core
.runtime
.SubMonitor
;
62 import org
.eclipse
.core
.runtime
.jobs
.Job
;
63 import org
.eclipse
.emf
.common
.command
.Command
;
64 import org
.eclipse
.emf
.common
.command
.CommandStack
;
65 import org
.eclipse
.emf
.common
.command
.CommandStackListener
;
66 import org
.eclipse
.emf
.common
.notify
.Adapter
;
67 import org
.eclipse
.emf
.common
.notify
.Notifier
;
68 import org
.eclipse
.emf
.common
.ui
.CommonUIPlugin
;
69 import org
.eclipse
.emf
.common
.util
.BasicDiagnostic
;
70 import org
.eclipse
.emf
.common
.util
.BasicMonitor
;
71 import org
.eclipse
.emf
.common
.util
.Diagnostic
;
72 import org
.eclipse
.emf
.compare
.Comparison
;
73 import org
.eclipse
.emf
.compare
.ConflictKind
;
74 import org
.eclipse
.emf
.compare
.Diff
;
75 import org
.eclipse
.emf
.compare
.DifferenceState
;
76 import org
.eclipse
.emf
.compare
.EMFCompare
;
77 import org
.eclipse
.emf
.compare
.EMFCompare
.Builder
;
78 import org
.eclipse
.emf
.compare
.Match
;
79 import org
.eclipse
.emf
.compare
.MatchResource
;
80 import org
.eclipse
.emf
.compare
.adapterfactory
.context
.IContextTester
;
81 import org
.eclipse
.emf
.compare
.command
.ICompareCopyCommand
;
82 import org
.eclipse
.emf
.compare
.domain
.ICompareEditingDomain
;
83 import org
.eclipse
.emf
.compare
.domain
.impl
.EMFCompareEditingDomain
;
84 import org
.eclipse
.emf
.compare
.ide
.internal
.utils
.DisposableResourceSet
;
85 import org
.eclipse
.emf
.compare
.ide
.internal
.utils
.NotLoadingResourceSet
;
86 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.EMFCompareIDEUIMessages
;
87 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.EMFCompareIDEUIPlugin
;
88 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.configuration
.EMFCompareConfiguration
;
89 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.MirrorUtil
;
90 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.label
.NoDifferencesCompareInput
;
91 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.label
.NoSelectedItemCompareInput
;
92 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.label
.NoVisibleItemCompareInput
;
93 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.label
.OnlyPseudoConflictsCompareInput
;
94 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.util
.EMFCompareColor
;
95 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.util
.RedoAction
;
96 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.contentmergeviewer
.util
.UndoAction
;
97 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.editor
.ComparisonScopeInput
;
98 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.logical
.ComparisonScopeBuilder
;
99 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.logical
.EmptyComparisonScope
;
100 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.logical
.StreamAccessorStorage
;
101 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.preferences
.EMFCompareUIPreferences
;
102 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.progress
.JobProgressInfoComposite
;
103 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.progress
.JobProgressMonitorWrapper
;
104 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
.EMFCompareStructureMergeViewerContentProvider
.FetchListener
;
105 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
.actions
.MergeAction
;
106 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
.actions
.MergeContainedNonConflictingAction
;
107 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
.provider
.TreeCompareInputAdapterFactory
;
108 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.structuremergeviewer
.provider
.TreeNodeCompareInput
;
109 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.util
.CompareHandlerService
;
110 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.util
.JFaceUtil
;
111 import org
.eclipse
.emf
.compare
.ide
.ui
.internal
.util
.PlatformElementUtil
;
112 import org
.eclipse
.emf
.compare
.internal
.merge
.MergeDataImpl
;
113 import org
.eclipse
.emf
.compare
.internal
.merge
.MergeMode
;
114 import org
.eclipse
.emf
.compare
.merge
.AbstractMerger
;
115 import org
.eclipse
.emf
.compare
.merge
.CachingDiffRelationshipComputer
;
116 import org
.eclipse
.emf
.compare
.merge
.IMergeOptionAware
;
117 import org
.eclipse
.emf
.compare
.merge
.IMerger
;
118 import org
.eclipse
.emf
.compare
.rcp
.EMFCompareRCPPlugin
;
119 import org
.eclipse
.emf
.compare
.rcp
.internal
.extension
.impl
.EMFCompareBuilderConfigurator
;
120 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.configuration
.ICompareEditingDomainChange
;
121 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.configuration
.IMergePreviewModeChange
;
122 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.configuration
.SideLabelProvider
;
123 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.mergeviewer
.IColorChangeEvent
;
124 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.structuremergeviewer
.filters
.StructureMergeViewerFilter
;
125 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.structuremergeviewer
.filters
.impl
.CascadingDifferencesFilter
;
126 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.structuremergeviewer
.groups
.StructureMergeViewerGrouper
;
127 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.structuremergeviewer
.groups
.provider
.TreeItemProviderAdapterFactorySpec
;
128 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.structuremergeviewer
.match
.MatchOfContainmentReferenceChangeProcessor
;
129 import org
.eclipse
.emf
.compare
.rcp
.ui
.internal
.util
.SWTUtil
;
130 import org
.eclipse
.emf
.compare
.rcp
.ui
.structuremergeviewer
.filters
.IDifferenceFilterChange
;
131 import org
.eclipse
.emf
.compare
.rcp
.ui
.structuremergeviewer
.groups
.IDifferenceGroup
;
132 import org
.eclipse
.emf
.compare
.rcp
.ui
.structuremergeviewer
.groups
.IDifferenceGroupProvider
;
133 import org
.eclipse
.emf
.compare
.rcp
.ui
.structuremergeviewer
.groups
.IDifferenceGroupProviderChange
;
134 import org
.eclipse
.emf
.compare
.scope
.IComparisonScope
;
135 import org
.eclipse
.emf
.compare
.utils
.EMFComparePredicates
;
136 import org
.eclipse
.emf
.compare
.utils
.IDiagnosable
;
137 import org
.eclipse
.emf
.ecore
.EObject
;
138 import org
.eclipse
.emf
.ecore
.resource
.Resource
;
139 import org
.eclipse
.emf
.ecore
.resource
.ResourceSet
;
140 import org
.eclipse
.emf
.ecore
.util
.EcoreUtil
;
141 import org
.eclipse
.emf
.edit
.provider
.ComposedAdapterFactory
;
142 import org
.eclipse
.emf
.edit
.provider
.IDisposable
;
143 import org
.eclipse
.emf
.edit
.provider
.ReflectiveItemProviderAdapterFactory
;
144 import org
.eclipse
.emf
.edit
.provider
.resource
.ResourceItemProviderAdapterFactory
;
145 import org
.eclipse
.emf
.edit
.tree
.TreeFactory
;
146 import org
.eclipse
.emf
.edit
.tree
.TreeNode
;
147 import org
.eclipse
.jface
.action
.IMenuListener
;
148 import org
.eclipse
.jface
.action
.IMenuManager
;
149 import org
.eclipse
.jface
.action
.MenuManager
;
150 import org
.eclipse
.jface
.preference
.IPreferenceStore
;
151 import org
.eclipse
.jface
.util
.IPropertyChangeListener
;
152 import org
.eclipse
.jface
.util
.PropertyChangeEvent
;
153 import org
.eclipse
.jface
.viewers
.AbstractTreeViewer
;
154 import org
.eclipse
.jface
.viewers
.DelegatingStyledCellLabelProvider
;
155 import org
.eclipse
.jface
.viewers
.ISelection
;
156 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
157 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
158 import org
.eclipse
.jface
.viewers
.ITreeViewerListener
;
159 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
160 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
161 import org
.eclipse
.jface
.viewers
.TreeExpansionEvent
;
162 import org
.eclipse
.jface
.viewers
.TreeViewer
;
163 import org
.eclipse
.swt
.SWT
;
164 import org
.eclipse
.swt
.custom
.CTabFolder
;
165 import org
.eclipse
.swt
.custom
.CTabItem
;
166 import org
.eclipse
.swt
.events
.ControlAdapter
;
167 import org
.eclipse
.swt
.events
.ControlEvent
;
168 import org
.eclipse
.swt
.events
.DisposeEvent
;
169 import org
.eclipse
.swt
.events
.FocusAdapter
;
170 import org
.eclipse
.swt
.events
.FocusEvent
;
171 import org
.eclipse
.swt
.graphics
.Color
;
172 import org
.eclipse
.swt
.graphics
.GC
;
173 import org
.eclipse
.swt
.graphics
.Point
;
174 import org
.eclipse
.swt
.graphics
.Rectangle
;
175 import org
.eclipse
.swt
.layout
.FillLayout
;
176 import org
.eclipse
.swt
.layout
.GridData
;
177 import org
.eclipse
.swt
.layout
.GridLayout
;
178 import org
.eclipse
.swt
.widgets
.Composite
;
179 import org
.eclipse
.swt
.widgets
.Control
;
180 import org
.eclipse
.swt
.widgets
.Display
;
181 import org
.eclipse
.swt
.widgets
.Event
;
182 import org
.eclipse
.swt
.widgets
.Listener
;
183 import org
.eclipse
.swt
.widgets
.Menu
;
184 import org
.eclipse
.swt
.widgets
.Tree
;
185 import org
.eclipse
.swt
.widgets
.TreeItem
;
186 import org
.eclipse
.team
.internal
.ui
.mapping
.ResourceDiffCompareInput
;
187 import org
.eclipse
.ui
.PlatformUI
;
188 import org
.eclipse
.ui
.actions
.ActionFactory
;
189 import org
.eclipse
.ui
.progress
.PendingUpdateAdapter
;
190 import org
.eclipse
.ui
.themes
.ITheme
;
191 import org
.eclipse
.ui
.themes
.IThemeManager
;
194 * Implementation of {@link AbstractStructuredViewerWrapper}.
196 * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a>
198 public class EMFCompareStructureMergeViewer
extends AbstractStructuredViewerWrapper
<CTabFolder
, WrappableTreeViewer
> implements CommandStackListener
{
200 private final class CompareInputChangedJob
extends Job
{
201 private CompareInputChangedJob(String name
) {
206 public IStatus
run(IProgressMonitor monitor
) {
207 IProgressMonitor wrapper
= new JobProgressMonitorWrapper(monitor
, progressInfoItem
);
208 SubMonitor subMonitor
= SubMonitor
.convert(wrapper
, EMFCompareIDEUIMessages
209 .getString("EMFCompareStructureMergeViewer.computingModelDifferences"), 100); //$NON-NLS-1$
211 compareInputChanged((ICompareInput
)getInput(), subMonitor
.newChild(100));
212 } catch (final OperationCanceledException e
) {
213 return Status
.CANCEL_STATUS
;
214 } catch (final Exception e
) {
215 EMFCompareIDEUIPlugin
.getDefault().log(e
);
217 subMonitor
.setWorkRemaining(0);
219 return Status
.OK_STATUS
;
223 private final class TitleBuilderJob
extends Job
{
225 public TitleBuilderJob() {
226 super("EMF Compare Title Builder"); //$NON-NLS-1$
231 protected IStatus
run(IProgressMonitor monitor
) {
232 final String title
= new TitleBuilder(getCompareConfiguration()).toString();
233 getContentProvider().runWhenReady(IN_UI_ASYNC
, new Runnable() {
235 CTabFolder control
= getControl();
236 if (!control
.isDisposed()) {
237 ((CompareViewerSwitchingPane
)control
.getParent()).setTitleArgument(title
);
241 return Status
.OK_STATUS
;
245 /** The width of the tree ruler. */
246 private static final int TREE_RULER_WIDTH
= 17;
248 private static final Function
<TreeNode
, Diff
> TREE_NODE_AS_DIFF
= new Function
<TreeNode
, Diff
>() {
249 public Diff
apply(TreeNode input
) {
250 if (input
.getData() instanceof Diff
) {
251 return (Diff
)input
.getData();
257 /** Preference store holding UI-related settings for this viewer. */
258 protected final IPreferenceStore preferenceStore
= EMFCompareIDEUIPlugin
.getDefault()
259 .getPreferenceStore();
261 /** The adapter factory. */
262 private ComposedAdapterFactory fAdapterFactory
;
264 /** The diff relationship computer. */
265 private CachingDiffRelationshipComputer fDiffRelationshipComputer
;
267 /** The tree ruler associated with this viewer. */
268 private EMFCompareDiffTreeRuler treeRuler
;
270 private final ICompareInputChangeListener fCompareInputChangeListener
;
272 /** The expand/collapse item listener. */
273 private ITreeViewerListener fWrappedTreeListener
;
275 /** The tree viewer. */
277 /** The undo action. */
278 private UndoAction undoAction
;
280 /** The redo action. */
281 private RedoAction redoAction
;
283 /** The compare handler service. */
284 private CompareHandlerService fHandlerService
;
287 * When comparing EObjects from a resource, the resource involved doesn't need to be unload by EMF
290 private boolean resourceSetShouldBeDisposed
;
292 private DependencyData dependencyData
;
294 private ISelectionChangedListener selectionChangeListener
;
296 /** The current selection. */
297 protected ISelection currentSelection
;
299 /** Listener reacting to changes in the {@link #preferenceStore}. */
300 protected IPropertyChangeListener preferenceChangeListener
;
302 private final Listener fEraseItemListener
;
304 private JobProgressInfoComposite progressInfoItem
;
306 private Job inputChangedTask
;
308 private final Job titleBuilderJob
= new TitleBuilderJob();
310 private CompareToolBar toolBar
;
312 private Navigatable navigatable
;
314 private EMFCompareColor fColors
;
316 private boolean editingDomainNeedsToBeDisposed
;
318 private FetchListener toolbarUpdaterContentProviderListener
;
320 private boolean cascadingDifferencesFilterEnabled
;
322 private IPropertyChangeListener fPreferenceChangeListener
;
324 private IPreferenceStore fPreferenceStore
;
330 * the SWT parent control under which to create the viewer's SWT control.
332 * a compare configuration the newly created viewer might want to use.
334 public EMFCompareStructureMergeViewer(Composite parent
, EMFCompareConfiguration config
) {
335 super(parent
, config
);
337 updateLayout(true, false);
339 StructureMergeViewerFilter structureMergeViewerFilter
= getCompareConfiguration()
340 .getStructureMergeViewerFilter();
341 getViewer().addFilter(structureMergeViewerFilter
);
343 StructureMergeViewerGrouper structureMergeViewerGrouper
= getCompareConfiguration()
344 .getStructureMergeViewerGrouper();
345 structureMergeViewerGrouper
.install(getViewer());
347 fCompareInputChangeListener
= new ICompareInputChangeListener() {
348 public void compareInputChanged(ICompareInput input
) {
349 EMFCompareStructureMergeViewer
.this.compareInputChanged(input
);
353 setContentProvider(new EMFCompareStructureMergeViewerContentProvider(
354 getCompareConfiguration().getAdapterFactory(), getViewer()));
356 navigatable
= new Navigatable(getViewer(), getContentProvider());
358 toolBar
= new CompareToolBar(CompareViewerPane
.getToolBarManager(parent
), structureMergeViewerGrouper
,
359 structureMergeViewerFilter
, getCompareConfiguration());
360 getViewer().addSelectionChangedListener(toolBar
);
364 selectionChangeListener
= new ISelectionChangedListener() {
365 public void selectionChanged(SelectionChangedEvent event
) {
366 handleSelectionChangedEvent(event
);
369 addSelectionChangedListener(selectionChangeListener
);
371 preferenceChangeListener
= new IPropertyChangeListener() {
372 public void propertyChange(PropertyChangeEvent event
) {
373 EMFCompareStructureMergeViewer
.this.handlePreferenceChangedEvent(event
);
376 preferenceStore
.addPropertyChangeListener(preferenceChangeListener
);
378 fWrappedTreeListener
= new ITreeViewerListener() {
379 public void treeExpanded(TreeExpansionEvent event
) {
383 public void treeCollapsed(TreeExpansionEvent event
) {
387 getViewer().addTreeListener(fWrappedTreeListener
);
389 fEraseItemListener
= new Listener() {
390 public void handleEvent(Event event
) {
391 handleEraseItemEvent(event
);
394 getViewer().getControl().addListener(SWT
.EraseItem
, fEraseItemListener
);
396 fHandlerService
= CompareHandlerService
.createFor(getCompareConfiguration().getContainer(),
397 getControl().getShell());
399 toolbarUpdaterContentProviderListener
= new FetchListener() {
402 public void startFetching() {
403 toolBar
.setEnabled(false);
407 public void doneFetching() {
408 toolBar
.setEnabled(true);
412 getContentProvider().addFetchingListener(toolbarUpdaterContentProviderListener
);
415 new DelegatingStyledCellLabelProvider(new EMFCompareStructureMergeViewerLabelProvider(
416 getCompareConfiguration().getAdapterFactory(), this)));
418 undoAction
= new UndoAction(getCompareConfiguration().getEditingDomain());
419 redoAction
= new RedoAction(getCompareConfiguration().getEditingDomain());
421 editingDomainChange(null, getCompareConfiguration().getEditingDomain());
423 inputChangedTask
.setPriority(Job
.LONG
);
425 config
.getEventBus().register(this);
427 final boolean enabled
= any(config
.getStructureMergeViewerFilter().getSelectedDifferenceFilters(),
428 instanceOf(CascadingDifferencesFilter
.class));
429 setCascadingDifferencesFilterEnabled(enabled
);
431 fPreferenceChangeListener
= new IPropertyChangeListener() {
433 public void propertyChange(PropertyChangeEvent event
) {
434 EMFCompareStructureMergeViewer
.this.handlePreferenceChangeEvent(event
);
438 fPreferenceStore
= getCompareConfiguration().getPreferenceStore();
439 if (fPreferenceStore
!= null) {
440 fPreferenceStore
.addPropertyChangeListener(fPreferenceChangeListener
);
444 protected void handlePreferenceChangeEvent(PropertyChangeEvent event
) {
445 if (MirrorUtil
.isMirroredPreference(event
.getProperty())) {
446 MirrorUtil
.setMirrored(getCompareConfiguration(),
447 Boolean
.parseBoolean(event
.getNewValue().toString()));
448 // Since the content merge viewer will only be recreated, we simulate a selection change to
449 // re-create the content merge viewer with correct sides
450 ISelection originalSelection
= getSelection();
453 getViewer().handleOpen(null); // parameter not used in super implementation -> null ok
455 setSelection(originalSelection
);
456 getViewer().handleOpen(null);
461 * The tool bar must be init after we know the editable state of left and right input.
463 * @see #compareInputChanged(ICompareInput, IProgressMonitor)
465 private void initToolbar(IProgressMonitor monitor
) {
466 if (!monitor
.isCanceled()) {
467 SWTUtil
.safeSyncExec(new Runnable() {
469 toolBar
.initToolbar(getViewer(), navigatable
);
470 toolBar
.setEnabled(false);
476 private void enableToolbar(IProgressMonitor monitor
) {
477 if (!monitor
.isCanceled()) {
478 SWTUtil
.safeSyncExec(new Runnable() {
480 toolBar
.setEnabled(true);
487 * Allow users to merge diffs through context menu.
489 private void createContextMenu() {
490 MenuManager menuMgr
= new MenuManager("#PopupMenu"); //$NON-NLS-1$
491 menuMgr
.setRemoveAllWhenShown(true);
492 menuMgr
.addMenuListener(new IMenuListener() {
493 public void menuAboutToShow(IMenuManager manager
) {
494 EMFCompareStructureMergeViewer
.this.fillContextMenu(manager
);
497 Menu menu
= menuMgr
.createContextMenu(getViewer().getControl());
498 getViewer().getControl().setMenu(menu
);
502 * Fill the context menu with the appropriate actions (ACCEPT/REJECT or LEFT TO RIGHT/RIGHT TO LEFT
503 * depending on the {@link org.eclipse.emf.compare.internal.merge.MergeMode}, and the write access of
507 * the context menu to fill.
509 private void fillContextMenu(IMenuManager manager
) {
510 if (!isOneMergeableItemSelected()) {
513 boolean leftEditable
= getCompareConfiguration().isLeftEditable();
514 boolean rightEditable
= getCompareConfiguration().isRightEditable();
515 final EnumSet
<MergeMode
> modes
;
516 if (rightEditable
&& leftEditable
) {
517 modes
= EnumSet
.of(MergeMode
.RIGHT_TO_LEFT
, MergeMode
.LEFT_TO_RIGHT
);
519 modes
= EnumSet
.of(MergeMode
.ACCEPT
, MergeMode
.REJECT
);
521 if (rightEditable
|| leftEditable
) {
522 for (MergeMode mode
: modes
) {
523 IMerger
.Registry mergerRegistry
= EMFCompareRCPPlugin
.getDefault().getMergerRegistry();
524 if (isOneDiffSelected()) {
525 MergeAction mergeAction
= new MergeAction(getCompareConfiguration(), mergerRegistry
, mode
,
526 navigatable
, (IStructuredSelection
)getSelection());
527 manager
.add(mergeAction
);
528 } else if (isOneMatchOrResourceMatchSelected()) {
529 final Predicate
<TreeNode
> filterPredicate
= new Predicate
<TreeNode
>() {
530 public boolean apply(TreeNode input
) {
532 && JFaceUtil
.isFiltered(getViewer(), input
, input
.getParent());
535 MergeContainedNonConflictingAction mergeAction
= new MergeContainedNonConflictingAction(
536 getCompareConfiguration(), mergerRegistry
, mode
, navigatable
,
537 (IStructuredSelection
)getSelection(), filterPredicate
);
538 manager
.add(mergeAction
);
545 * Check if the item selected in this viewer is mergeable; that is, if a {@link Diff}, a {@link Match}, or
546 * {@link MatchResource} is selected.
548 * @return true if the item selected is mergeable, false otherwise.
550 private boolean isOneMergeableItemSelected() {
551 return isOneDiffSelected() || isOneMatchOrResourceMatchSelected();
555 * Specifies whether the a {@link Match} or a {@link MatchResource} is currently selected in this viewer.
557 * @return <code>true</code> if an instance of a {@link Match} or a {@link MatchResource} is selected,
558 * <code>false</code> otherwise.
560 private boolean isOneDiffSelected() {
561 final ISelection selection
= getSelection();
562 if (selection
instanceof IStructuredSelection
&& ((IStructuredSelection
)selection
).size() == 1) {
563 Object element
= ((IStructuredSelection
)selection
).getFirstElement();
564 if (getDataOfTreeNodeOfAdapter(element
) instanceof Diff
) {
572 * Specifies whether the a {@link Match} or a {@link MatchResource} is currently selected in this viewer.
574 * @return <code>true</code> if an instance of a {@link Match} or a {@link MatchResource} is selected,
575 * <code>false</code> otherwise.
577 private boolean isOneMatchOrResourceMatchSelected() {
578 final ISelection selection
= getSelection();
579 if (selection
instanceof IStructuredSelection
&& ((IStructuredSelection
)selection
).size() == 1) {
580 Object element
= ((IStructuredSelection
)selection
).getFirstElement();
581 if (isMatchOrMatchResource(getDataOfTreeNodeOfAdapter(element
))) {
589 * Specifies whether the given {@code eObject} is a {@link Match} or a {@link MatchResource}.
592 * The EObject to check.
593 * @return <code>true</code> if it is an instance a {@link Match} or a {@link MatchResource},
594 * <code>false</code> otherwise.
596 private boolean isMatchOrMatchResource(EObject eObject
) {
597 return eObject
instanceof Match
|| eObject
instanceof MatchResource
;
603 * @see org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.AbstractViewerWrapper#preHookCreateControlAndViewer()
606 protected void preHookCreateControlAndViewer() {
607 fAdapterFactory
= initAdapterFactory(getCompareConfiguration().getComparison());
608 getCompareConfiguration().setAdapterFactory(fAdapterFactory
);
610 fDiffRelationshipComputer
= new CachingDiffRelationshipComputer(
611 EMFCompareRCPPlugin
.getDefault().getMergerRegistry());
612 getCompareConfiguration().setDiffRelationshipComputer(fDiffRelationshipComputer
);
614 inputChangedTask
= new CompareInputChangedJob(EMFCompareIDEUIMessages
615 .getString("EMFCompareStructureMergeViewer.computingModelDifferences")); //$NON-NLS-1$
619 * Creates a new adapter factory based on the current compare configuration.
621 * @return adapter factory
623 protected ComposedAdapterFactory
initAdapterFactory(Comparison comparison
) {
624 Map
<Object
, Object
> context
= Maps
.newLinkedHashMap();
625 context
.put(IContextTester
.CTX_COMPARISON
, comparison
);
627 ComposedAdapterFactory adapterFactory
= new ComposedAdapterFactory(
628 EMFCompareRCPPlugin
.getDefault().createFilteredAdapterFactoryRegistry(context
));
629 adapterFactory
.addAdapterFactory(new TreeItemProviderAdapterFactorySpec(
630 getCompareConfiguration().getStructureMergeViewerFilter()));
631 adapterFactory
.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
632 adapterFactory
.addAdapterFactory(new ResourceItemProviderAdapterFactory());
633 return adapterFactory
;
637 public void colorChanged(
638 @SuppressWarnings("unused") /* necessary for @Subscribe */IColorChangeEvent changeColorEvent
) {
645 * @see org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.ViewerWrapper.createControl(
646 * Composite, CompareConfiguration)
649 protected ControlAndViewer
<CTabFolder
, WrappableTreeViewer
> createControlAndViewer(Composite parent
) {
650 parent
.setLayout(new FillLayout());
651 CTabFolder tabFolder
= new CTabFolder(parent
, SWT
.BOTTOM
| SWT
.FLAT
);
652 tabFolder
.setLayout(new FillLayout());
654 // Ensures that this viewer will only display the page's tab
655 // area if there are more than one page
657 tabFolder
.addControlListener(new ControlAdapter() {
658 boolean guard
= false;
661 public void controlResized(ControlEvent event
) {
670 updateProblemIndication(Diagnostic
.OK_INSTANCE
);
672 Composite control
= new Composite(tabFolder
, SWT
.NONE
);
673 createItem(0, control
);
674 tabFolder
.setSelection(0);
676 GridData data
= new GridData(SWT
.FILL
, SWT
.FILL
, true, true);
677 control
.setLayoutData(data
);
679 GridLayout layout
= new GridLayout(2, false);
680 layout
.marginWidth
= 0;
681 layout
.marginHeight
= 0;
682 layout
.horizontalSpacing
= 0;
683 layout
.verticalSpacing
= 0;
684 control
.setLayout(layout
);
686 progressInfoItem
= new JobProgressInfoComposite(inputChangedTask
, control
,
687 SWT
.SMOOTH
| SWT
.HORIZONTAL
, SWT
.NONE
);
688 progressInfoItem
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true, 2, 1));
689 progressInfoItem
.setBackground(Display
.getDefault().getSystemColor(SWT
.COLOR_LIST_BACKGROUND
));
691 final WrappableTreeViewer treeViewer
= new WrappableTreeViewer(control
,
692 SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
) {
696 * @see org.eclipse.jface.viewers.TreeViewer#isExpandable(java.lang.Object)
699 public boolean isExpandable(Object element
) {
700 if (element
instanceof PendingUpdateAdapter
) {
701 // Prevents requesting the content provider if the object is a PendingUpdateAdapter
705 // workaround for 65762
706 return getFilteredChildren(element
).length
> 0;
708 return super.isExpandable(element
);
711 treeViewer
.getControl().setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
712 treeViewer
.setUseHashlookup(true);
714 treeViewer
.getControl().addFocusListener(new FocusAdapter() {
716 public void focusGained(FocusEvent e
) {
717 fHandlerService
.updatePaneActionHandlers(new Runnable() {
719 fHandlerService
.setGlobalActionHandler(ActionFactory
.UNDO
.getId(), undoAction
);
720 fHandlerService
.setGlobalActionHandler(ActionFactory
.REDO
.getId(), redoAction
);
726 dependencyData
= new DependencyData(getCompareConfiguration());
728 tabFolder
.setData(CompareUI
.COMPARE_VIEWER_TITLE
,
729 EMFCompareIDEUIMessages
.getString("EMFCompareStructureMergeViewer.title")); //$NON-NLS-1$
731 final ITheme currentTheme
= getCurrentTheme();
733 boolean leftIsLocal
= getCompareConfiguration().getBooleanProperty("LEFT_IS_LOCAL", false); //$NON-NLS-1$
734 fColors
= new EMFCompareColor(control
.getDisplay(), leftIsLocal
, currentTheme
,
735 getCompareConfiguration().getEventBus());
737 treeRuler
= new EMFCompareDiffTreeRuler(control
, SWT
.NONE
, treeViewer
, dependencyData
, fColors
);
738 GridData rulerLayoutData
= new GridData(SWT
.FILL
, SWT
.FILL
, false, true);
739 rulerLayoutData
.exclude
= true;
740 rulerLayoutData
.widthHint
= TREE_RULER_WIDTH
;
741 rulerLayoutData
.minimumWidth
= TREE_RULER_WIDTH
;
742 treeRuler
.setLayoutData(rulerLayoutData
);
744 return ControlAndViewer
.create(tabFolder
, treeViewer
);
748 * Determines the current used theme.
750 * @return The currently used theme if available, {@code null} otherwise.
752 private ITheme
getCurrentTheme() {
753 if (PlatformUI
.isWorkbenchRunning()) {
754 final IThemeManager themeManager
= PlatformUI
.getWorkbench().getThemeManager();
755 if (themeManager
!= null) {
756 return themeManager
.getCurrentTheme();
765 * @see org.eclipse.jface.viewers.ContentViewer#getContentProvider()
768 public EMFCompareStructureMergeViewerContentProvider
getContentProvider() {
769 return (EMFCompareStructureMergeViewerContentProvider
)super.getContentProvider();
773 public DelegatingStyledCellLabelProvider
getLabelProvider() {
774 return (DelegatingStyledCellLabelProvider
)super.getLabelProvider();
777 private CTabItem
createItem(int index
, Control control
) {
778 CTabItem item
= new CTabItem((CTabFolder
)control
.getParent(), SWT
.NONE
, index
);
779 item
.setControl(control
);
784 public void handleEditingDomainChange(ICompareEditingDomainChange event
) {
785 editingDomainChange(event
.getOldValue(), event
.getNewValue());
788 private void editingDomainChange(ICompareEditingDomain oldValue
, ICompareEditingDomain newValue
) {
789 if (newValue
!= oldValue
) {
790 if (oldValue
!= null) {
791 oldValue
.getCommandStack().removeCommandStackListener(this);
794 if (newValue
!= null) {
795 newValue
.getCommandStack().addCommandStackListener(this);
798 undoAction
.setEditingDomain(newValue
);
799 redoAction
.setEditingDomain(newValue
);
803 private void refreshTitle() {
804 if (getControl().isDisposed() || !(getControl().getParent() instanceof CompareViewerSwitchingPane
)) {
808 if (getCompareConfiguration().getComparison() == null) {
812 // Schedule with a short delay, because refreshTitle is often called multiple times quickly and this
813 // way, with a short delay, the job is run only once even in that case.
814 titleBuilderJob
.schedule(10L);
817 static EObject
getDataOfTreeNodeOfAdapter(Object object
) {
819 if (object
instanceof Adapter
) {
820 Notifier target
= ((Adapter
)object
).getTarget();
821 if (target
instanceof TreeNode
) {
822 data
= ((TreeNode
)target
).getData();
829 public void mergePreviewModeChange(@SuppressWarnings("unused") IMergePreviewModeChange event
) {
830 SWTUtil
.safeAsyncExec(new Runnable() {
832 updateHighlightRelatedChanges(getSelection());
838 public void handleDifferenceFilterChange(IDifferenceFilterChange event
) {
839 final boolean enabled
= any(event
.getSelectedDifferenceFilters(),
840 instanceOf(CascadingDifferencesFilter
.class));
841 setCascadingDifferencesFilterEnabled(enabled
);
842 SWTUtil
.safeRefresh(this, false, true);
843 getContentProvider().runWhenReady(IN_UI_ASYNC
, new Runnable() {
845 if (navigatable
!= null && (navigatable
.getViewer().getSelection() == null
846 || navigatable
.getViewer().getSelection().isEmpty())) {
847 selectFirstDiffOrDisplayLabelViewer(getCompareConfiguration().getComparison());
854 * Set the state of the cascading filter.
857 * true if the filter is enabled, false otherwise.
859 private void setCascadingDifferencesFilterEnabled(boolean enable
) {
860 this.cascadingDifferencesFilterEnabled
= enable
;
861 IMerger
.Registry mergerRegistry
= EMFCompareRCPPlugin
.getDefault().getMergerRegistry();
862 for (IMergeOptionAware merger
: Iterables
.filter(mergerRegistry
.getMergers(null),
863 IMergeOptionAware
.class)) {
864 Map
<Object
, Object
> mergeOptions
= merger
.getMergeOptions();
865 mergeOptions
.put(AbstractMerger
.SUB_DIFF_AWARE_OPTION
, Boolean
.valueOf(enable
));
870 * Get the state of the cascading filter.
872 * @return true if the filter is enabled, false otherwise.
874 private boolean getCascadingDifferencesFilterEnabled() {
875 return this.cascadingDifferencesFilterEnabled
;
879 public void handleDifferenceGroupProviderChange(
880 @SuppressWarnings("unused") IDifferenceGroupProviderChange event
) {
881 SWTUtil
.safeRefresh(this, false, true);
882 getContentProvider().runWhenReady(IN_UI_ASYNC
, new Runnable() {
884 selectFirstDiffOrDisplayLabelViewer(getCompareConfiguration().getComparison());
892 * @see org.eclipse.jface.viewers.Viewer#inputChanged(Object, Object)
895 protected void inputChanged(Object input
, Object oldInput
) {
896 if (oldInput
instanceof ICompareInput
) {
897 ICompareInput old
= (ICompareInput
)oldInput
;
898 old
.removeCompareInputChangeListener(fCompareInputChangeListener
);
901 if (input
instanceof ICompareInput
) {
902 ICompareInput ci
= (ICompareInput
)input
;
903 ci
.addCompareInputChangeListener(fCompareInputChangeListener
);
904 compareInputChanged(ci
);
911 * @see org.eclipse.emf.compare.ide.ui.internal.structuremergeviewer.AbstractViewerWrapper#handleDispose(DisposeEvent)
914 protected void handleDispose(DisposeEvent event
) {
915 if (fHandlerService
!= null) {
916 fHandlerService
.dispose();
918 getCompareConfiguration().getEventBus().unregister(this);
919 getViewer().removeTreeListener(fWrappedTreeListener
);
920 Object input
= getInput();
921 if (input
instanceof ICompareInput
) {
922 ICompareInput ci
= (ICompareInput
)input
;
923 ci
.removeCompareInputChangeListener(fCompareInputChangeListener
);
925 removeSelectionChangedListener(selectionChangeListener
);
926 getViewer().removeSelectionChangedListener(toolBar
);
927 getViewer().getTree().removeListener(SWT
.EraseItem
, fEraseItemListener
);
928 if (preferenceChangeListener
!= null) {
929 preferenceStore
.removePropertyChangeListener(preferenceChangeListener
);
931 if (editingDomainNeedsToBeDisposed
) {
932 ((IDisposable
)getCompareConfiguration().getEditingDomain()).dispose();
934 getCompareConfiguration().getStructureMergeViewerGrouper().uninstall(getViewer());
935 compareInputChanged((ICompareInput
)null);
936 treeRuler
.handleDispose();
937 fAdapterFactory
.dispose();
938 fDiffRelationshipComputer
.invalidate();
942 if (fPreferenceChangeListener
!= null) {
943 if (fPreferenceStore
!= null) {
944 fPreferenceStore
.removePropertyChangeListener(fPreferenceChangeListener
);
946 fPreferenceChangeListener
= null;
948 super.handleDispose(event
);
954 * @see org.eclipse.emf.common.command.CommandStackListener#commandStackChanged(java.util.EventObject)
956 public void commandStackChanged(EventObject event
) {
960 Command mostRecentCommand
= ((CommandStack
)event
.getSource()).getMostRecentCommand();
961 if (mostRecentCommand
instanceof ICompareCopyCommand
) {
962 // MUST NOT call a setSelection with a list, o.e.compare does not handle it (cf
963 // org.eclipse.compare.CompareEditorInput#getElement(ISelection))
964 Collection
<?
> affectedObjects
= mostRecentCommand
.getAffectedObjects();
965 TreeNode unfilteredNode
= null;
966 if (!affectedObjects
.isEmpty()) {
967 final Iterator
<EObject
> affectedIterator
= Iterables
.filter(affectedObjects
, EObject
.class)
969 IDifferenceGroupProvider groupProvider
= getCompareConfiguration()
970 .getStructureMergeViewerGrouper().getProvider();
971 while (affectedIterator
.hasNext() && unfilteredNode
== null) {
972 EObject affected
= affectedIterator
.next();
973 Iterable
<TreeNode
> treeNodes
= groupProvider
.getTreeNodes(affected
);
974 for (TreeNode node
: treeNodes
) {
975 if (!JFaceUtil
.isFiltered(getViewer(), node
, node
.getParent())) {
976 unfilteredNode
= node
;
982 if (unfilteredNode
!= null) {
983 final Object adaptedAffectedObject
= fAdapterFactory
.adapt(unfilteredNode
,
984 ICompareInput
.class);
985 // be sure the affected object has been created in the viewer.
986 for (TreeNode node
: getPath(null, unfilteredNode
)) {
987 getViewer().expandToLevel(fAdapterFactory
.adapt(node
, ICompareInput
.class), 0);
989 // execute synchronously the set selection to be sure the MergeAction#run() will
990 // select next diff after.
991 SWTUtil
.safeSyncExec(new Runnable() {
994 StructuredSelection selection
= new StructuredSelection(adaptedAffectedObject
);
995 // allows to call CompareToolBar#selectionChanged(SelectionChangedEvent)
996 getViewer().setSelection(selection
);
999 // update content viewers with the new selection
1000 SWTUtil
.safeAsyncExec(new Runnable() {
1002 navigatable
.openSelectedChange();
1007 // FIXME, should recompute the difference, something happened outside of this compare editor
1012 private Iterable
<TreeNode
> getPath(TreeNode from
, TreeNode to
) {
1014 return Collections
.emptyList();
1017 final List
<TreeNode
> path
= new ArrayList
<TreeNode
>();
1019 TreeNode parent
= to
.getParent();
1020 while (parent
!= null && parent
!= from
) {
1022 parent
= parent
.getParent();
1024 return Lists
.reverse(path
);
1028 * Triggered by fCompareInputChangeListener and {@link #inputChanged(Object, Object)}.
1030 void compareInputChanged(ICompareInput input
) {
1031 if (input
== null) {
1032 // When closing, we don't need a progress monitor to handle the input change
1033 compareInputChanged((ICompareInput
)null, new NullProgressMonitor());
1036 // The compare configuration is nulled when the viewer is disposed
1037 if (getCompareConfiguration() != null) {
1038 updateLayout(true, true);
1039 inputChangedTask
.schedule();
1043 void compareInputChanged(CompareInputAdapter input
, IProgressMonitor monitor
) {
1044 compareInputChanged(null, (Comparison
)input
.getComparisonObject(), monitor
);
1047 void compareInputChanged(ComparisonScopeInput input
, IProgressMonitor monitor
) {
1048 if (monitor
.isCanceled()) {
1051 IComparisonScope comparisonScope
= input
.getComparisonScope();
1052 EMFCompareConfiguration compareConfiguration
= getCompareConfiguration();
1054 EMFCompare comparator
= compareConfiguration
.getEMFComparator();
1055 compareConfiguration
.setLeftEditable(input
.isLeftEditable());
1056 compareConfiguration
.setRightEditable(input
.isRightEditable());
1058 if (input
.isLeftEditable() && input
.isRightEditable()) {
1059 compareConfiguration
.setMergePreviewMode(MergeMode
.RIGHT_TO_LEFT
);
1061 compareConfiguration
.setMergePreviewMode(MergeMode
.ACCEPT
);
1065 if (compareConfiguration
.getEditingDomain() == null) {
1066 ICompareEditingDomain domain
= EMFCompareEditingDomain
.create(comparisonScope
.getLeft(),
1067 comparisonScope
.getRight(), comparisonScope
.getOrigin());
1068 compareConfiguration
.setEditingDomain(domain
);
1070 if (comparator
== null) {
1071 Builder builder
= EMFCompare
.builder();
1072 EMFCompareBuilderConfigurator
.createDefault().configure(builder
);
1073 comparator
= builder
.build();
1076 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, 10);
1077 final Comparison comparison
= comparator
.compare(comparisonScope
,
1078 BasicMonitor
.toMonitor(subMonitor
.newChild(10)));
1080 // Bug 458802: NPE when synchronizing SMV & CMV if comparison is empty
1081 hookAdapters(input
, comparison
);
1083 compareInputChanged(input
.getComparisonScope(), comparison
, monitor
);
1086 void compareInputChanged(final IComparisonScope scope
, final Comparison comparison
,
1087 final IProgressMonitor monitor
) {
1088 if (!getControl().isDisposed() && !monitor
.isCanceled()) { // guard against disposal
1089 final EMFCompareConfiguration config
= getCompareConfiguration();
1091 ComposedAdapterFactory oldAdapterFactory
= fAdapterFactory
;
1092 // re-initialize adapter factory due to new comparison
1093 fAdapterFactory
= initAdapterFactory(comparison
);
1095 // clear cache for new comparison
1096 if (fDiffRelationshipComputer
!= null) {
1097 fDiffRelationshipComputer
.invalidate();
1100 // propagate new adapter factory
1101 config
.setAdapterFactory(fAdapterFactory
);
1102 getContentProvider().setAdapterFactory(fAdapterFactory
);
1103 ((EMFCompareStructureMergeViewerLabelProvider
)getLabelProvider().getStyledStringProvider())
1104 .setAdapterFactory(fAdapterFactory
);
1106 final TreeNode treeNode
= TreeFactory
.eINSTANCE
.createTreeNode();
1107 treeNode
.setData(comparison
);
1108 final Object input
= fAdapterFactory
.adapt(treeNode
, ICompareInput
.class);
1110 // this will set to the EMPTY difference group provider, but necessary to avoid NPE while
1112 IDifferenceGroupProvider groupProvider
= config
.getStructureMergeViewerGrouper().getProvider();
1113 treeNode
.eAdapters().add(groupProvider
);
1115 // display problem tabs if any
1116 if (!monitor
.isCanceled()) {
1117 SWTUtil
.safeAsyncExec(new Runnable() {
1119 Diagnostic diagnostic
= comparison
.getDiagnostic();
1120 if (diagnostic
== null) {
1121 updateProblemIndication(Diagnostic
.OK_INSTANCE
);
1123 updateProblemIndication(diagnostic
);
1129 // must set the input now in a synchronous mean. It will be used in the #setComparisonAndScope
1130 // afterwards during the initialization of StructureMergeViewerFilter and
1131 // StructureMergeViewerGrouper.
1132 if (!monitor
.isCanceled()) {
1133 SWTUtil
.safeSyncExec(new Runnable() {
1135 getViewer().setInput(input
);
1140 config
.setComparisonAndScope(comparison
, scope
);
1142 SWTUtil
.safeAsyncExec(new Runnable() {
1145 if (!getControl().isDisposed()) {
1146 updateLayout(false, true);
1151 getContentProvider().runWhenReady(IN_UI_ASYNC
, new Runnable() {
1153 if (!getControl().isDisposed()) {
1154 // title is not initialized as the comparison was set in the configuration after
1155 // the refresh caused by the initialization of the viewer filters and the group
1159 // Expands the tree viewer to the default expansion level
1160 expandTreeToLevel(getDefaultTreeExpansionLevel());
1162 // Selects the first difference once the tree has been filled.
1163 selectFirstDiffOrDisplayLabelViewer(comparison
);
1168 SWTUtil
.safeAsyncExec(new Runnable() {
1170 fHandlerService
.updatePaneActionHandlers(new Runnable() {
1172 fHandlerService
.setGlobalActionHandler(ActionFactory
.UNDO
.getId(), undoAction
);
1173 fHandlerService
.setGlobalActionHandler(ActionFactory
.REDO
.getId(), redoAction
);
1179 // Disposing this at the start of this method would meet frequent CME
1180 if (oldAdapterFactory
!= null) {
1181 oldAdapterFactory
.dispose();
1186 void compareInputChanged(final ICompareInput input
, IProgressMonitor monitor
) {
1187 if (input
!= null && !monitor
.isCanceled()) {
1188 if (input
instanceof CompareInputAdapter
) {
1189 resourceSetShouldBeDisposed
= false;
1190 compareInputChanged((CompareInputAdapter
)input
, monitor
);
1191 initToolbar(monitor
);
1192 } else if (input
instanceof ComparisonScopeInput
) {
1193 resourceSetShouldBeDisposed
= false;
1194 compareInputChanged((ComparisonScopeInput
)input
, monitor
);
1195 initToolbar(monitor
);
1197 resourceSetShouldBeDisposed
= true;
1198 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, 100);
1200 final ITypedElement left
= input
.getLeft();
1201 final ITypedElement right
= input
.getRight();
1202 final ITypedElement origin
= input
.getAncestor();
1204 final boolean leftEditable
;
1205 final boolean rightEditable
;
1207 EMFCompareConfiguration compareConfiguration
= getCompareConfiguration();
1209 * A resource node means that the left ITypedElement is in the workspace, a DiffNode input
1210 * means the comparison has been launched from a Replace With action.
1212 if (left
instanceof ResourceNode
&& !(input
instanceof DiffNode
)) {
1213 ResourceAttributes attributes
= ((ResourceNode
)left
).getResource()
1214 .getResourceAttributes();
1215 leftEditable
= attributes
!= null && !attributes
.isReadOnly();
1217 leftEditable
= compareConfiguration
.isLeftEditable();
1220 if (right
instanceof ResourceNode
) {
1221 ResourceAttributes attributes
= ((ResourceNode
)right
).getResource()
1222 .getResourceAttributes();
1223 rightEditable
= attributes
!= null && !attributes
.isReadOnly();
1225 rightEditable
= compareConfiguration
.isRightEditable();
1228 compareConfiguration
.setLeftEditable(leftEditable
);
1229 compareConfiguration
.setRightEditable(rightEditable
);
1231 if (leftEditable
&& rightEditable
) {
1232 compareConfiguration
.setMergePreviewMode(MergeMode
.RIGHT_TO_LEFT
);
1234 compareConfiguration
.setMergePreviewMode(MergeMode
.ACCEPT
);
1237 final BasicDiagnostic diagnostic
= new BasicDiagnostic(Diagnostic
.OK
,
1238 EMFCompareIDEUIPlugin
.PLUGIN_ID
, 0, null, new Object
[0]);
1239 IComparisonScope scope
= null;
1242 scope
= ComparisonScopeBuilder
.create(compareConfiguration
.getContainer(), left
, right
,
1243 origin
, subMonitor
.newChild(85));
1244 } catch (OperationCanceledException e
) {
1245 scope
= new EmptyComparisonScope();
1246 ((BasicDiagnostic
)((EmptyComparisonScope
)scope
).getDiagnostic())
1247 .merge(new BasicDiagnostic(Diagnostic
.CANCEL
, EMFCompareIDEUIPlugin
.PLUGIN_ID
, 0,
1248 EMFCompareIDEUIMessages
1249 .getString("EMFCompareStructureMergeViewer.operationCanceled"), //$NON-NLS-1$
1250 new Object
[] {e
, }));
1251 } catch (Exception e
) {
1252 scope
= new EmptyComparisonScope();
1253 ((BasicDiagnostic
)((EmptyComparisonScope
)scope
).getDiagnostic())
1254 .merge(BasicDiagnostic
.toDiagnostic(e
));
1255 EMFCompareIDEUIPlugin
.getDefault().log(e
);
1258 if (scope
instanceof IDiagnosable
&& ((IDiagnosable
)scope
).getDiagnostic() != null) {
1259 diagnostic
.merge(((IDiagnosable
)scope
).getDiagnostic());
1262 final Builder comparisonBuilder
= EMFCompare
.builder();
1264 EMFCompareBuilderConfigurator
.createDefault().configure(comparisonBuilder
);
1266 SubMonitor subMonitorChild
= SubMonitor
.convert(subMonitor
.newChild(15), 10);
1267 final Comparison compareResult
= comparisonBuilder
.build().compare(scope
,
1268 BasicMonitor
.toMonitor(subMonitorChild
));
1270 hookAdapters(input
, compareResult
);
1272 if (compareResult
.getDiagnostic() != null) {
1273 diagnostic
.merge(compareResult
.getDiagnostic());
1275 // update diagnostic of the comparison with the global one.
1276 compareResult
.setDiagnostic(diagnostic
);
1278 final ResourceSet leftResourceSet
= (ResourceSet
)scope
.getLeft();
1279 final ResourceSet rightResourceSet
= (ResourceSet
)scope
.getRight();
1280 final ResourceSet originResourceSet
= (ResourceSet
)scope
.getOrigin();
1282 ICompareEditingDomain editingDomain
= EMFCompareEditingDomain
.create(leftResourceSet
,
1283 rightResourceSet
, originResourceSet
);
1284 editingDomainNeedsToBeDisposed
= true;
1285 compareConfiguration
.setEditingDomain(editingDomain
);
1287 if (leftResourceSet
instanceof NotLoadingResourceSet
) {
1288 ((NotLoadingResourceSet
)leftResourceSet
).setAllowResourceLoad(true);
1290 if (rightResourceSet
instanceof NotLoadingResourceSet
) {
1291 ((NotLoadingResourceSet
)rightResourceSet
).setAllowResourceLoad(true);
1293 if (originResourceSet
instanceof NotLoadingResourceSet
) {
1294 ((NotLoadingResourceSet
)originResourceSet
).setAllowResourceLoad(true);
1297 IStorage leftStorage
= PlatformElementUtil
.findFile(left
);
1298 if (leftStorage
== null) {
1299 leftStorage
= StreamAccessorStorage
.fromTypedElement(left
);
1302 initToolbar(monitor
);
1303 compareInputChanged(scope
, compareResult
, monitor
);
1305 // Protect compare actions from over-enthusiast users
1306 enableToolbar(monitor
);
1308 compareInputChangedToNull();
1313 * Hooks the adapters required for handling UI properly.
1316 * @param compareResult
1318 private void hookAdapters(final ICompareInput input
, final Comparison compareResult
) {
1319 compareResult
.eAdapters().add(new ForwardingCompareInputAdapter(input
));
1321 // org.eclipse.team.internal.ui.mapping.ResourceCompareInputChangeNotifier$CompareInputLabelProvider
1322 // who doesn't check a cast in its getAncestorLabel(), getLeftLabel() and getRightLabel() methods,
1323 // we can't allow to add side label provider in case of an input of type ResourceDiffCompareInput.
1324 if (!(input
instanceof ResourceDiffCompareInput
)) {
1325 ICompareInputLabelProvider labelProvider
= getCompareConfiguration().getLabelProvider();
1326 SideLabelProvider sideLabelProvider
= new SideLabelProvider(labelProvider
.getAncestorLabel(input
),
1327 labelProvider
.getLeftLabel(input
), labelProvider
.getRightLabel(input
),
1328 labelProvider
.getAncestorImage(input
), labelProvider
.getLeftImage(input
),
1329 labelProvider
.getRightImage(input
));
1330 compareResult
.eAdapters().add(sideLabelProvider
);
1332 // Bug 501569: The cascading filter does not hide merged cascading diffs
1333 new MatchOfContainmentReferenceChangeProcessor().execute(compareResult
);
1335 // Add a MergeData to handle status decorations on Diffs
1336 MergeDataImpl mergeData
= new MergeDataImpl(getCompareConfiguration().isLeftEditable(),
1337 getCompareConfiguration().isRightEditable());
1338 compareResult
.eAdapters().add(mergeData
);
1342 * Returns whether the first change should be selected automatically after initialization.
1344 * @return true if the first change should be selected automatically, false otherwise.
1345 * @see #selectFirstDiffOrDisplayLabelViewer(Comparison)
1347 protected boolean isSelectFirstChange() {
1348 return preferenceStore
.getBoolean(EMFCompareUIPreferences
.EDITOR_TREE_AUTO_SELECT_FIRST_CHANGE
);
1352 * Returns the default expansion level for the tree viewer.
1354 * @return non-negative level, or {@link AbstractTreeViewer#ALL_LEVELS ALL_LEVELS} to expand all levels of
1356 * @see #expandTreeToLevel(int)
1358 protected int getDefaultTreeExpansionLevel() {
1359 return preferenceStore
.getInt(EMFCompareUIPreferences
.EDITOR_TREE_AUTO_EXPAND_LEVEL
);
1363 * Expands the {@link #getViewer() tree viewer} to the given level.
1366 * non-negative level, or {@link AbstractTreeViewer#ALL_LEVELS ALL_LEVELS} to expand all levels
1368 * @see TreeViewer#expandToLevel(int)
1370 protected void expandTreeToLevel(int level
) {
1371 getViewer().expandToLevel(level
);
1375 * Returns whether we highlight changes related to the current selected change.
1377 * @return true if we highlight related changes, false otherwise.
1378 * @see #updateHighlightRelatedChanges(ISelection)
1380 protected boolean isHighlightRelatedChanges() {
1381 return preferenceStore
.getBoolean(EMFCompareUIPreferences
.EDITOR_TREE_HIGHLIGHT_RELATED_CHANGES
);
1385 * Updates the highlighting of related changes for the current selection, if it is
1386 * {@link #isHighlightRelatedChanges() enabled}.
1391 protected void updateHighlightRelatedChanges(ISelection selection
) {
1392 if (!isHighlightRelatedChanges()) {
1395 dependencyData
.updateDependencies(selection
, EMFCompareRCPPlugin
.getDefault().getMergerRegistry());
1400 * Clears the highlighting of related changes for the current selection.
1402 protected void clearHighlightRelatedChanges() {
1403 dependencyData
.clearDependencies();
1408 * Select the first difference...if there are differences, otherwise, display appropriate content viewer
1409 * (no differences or no visible differences)
1412 * the comparison used to know if there are differences.
1414 private void selectFirstDiffOrDisplayLabelViewer(final Comparison comparison
) {
1415 if (comparison
!= null) {
1416 ICompareInput compareInput
= (ICompareInput
)EcoreUtil
.getAdapter(comparison
.eAdapters(),
1417 ICompareInput
.class);
1418 if (compareInput
== null) {
1419 compareInput
= new TreeNodeCompareInput(new TreeCompareInputAdapterFactory());
1421 List
<Diff
> differences
= comparison
.getDifferences();
1422 if (differences
.isEmpty()) {
1423 navigatable
.fireOpen(new NoDifferencesCompareInput(compareInput
));
1424 } else if (!navigatable
.hasChange(INavigatable
.FIRST_CHANGE
)) {
1425 if (hasOnlyPseudoConflicts(differences
)) {
1426 navigatable
.fireOpen(new OnlyPseudoConflictsCompareInput(compareInput
));
1428 navigatable
.fireOpen(new NoVisibleItemCompareInput(compareInput
));
1430 } else if (!isSelectFirstChange()) {
1431 navigatable
.fireOpen(new NoSelectedItemCompareInput(compareInput
));
1433 navigatable
.selectChange(INavigatable
.FIRST_CHANGE
);
1438 private boolean hasOnlyPseudoConflicts(List
<Diff
> differences
) {
1439 return Iterators
.all(differences
.iterator(), EMFComparePredicates
.hasConflict(ConflictKind
.PSEUDO
));
1442 private void updateLayout(boolean displayProgress
, boolean doLayout
) {
1443 ((GridData
)progressInfoItem
.getLayoutData()).exclude
= !displayProgress
;
1444 progressInfoItem
.setVisible(displayProgress
);
1446 ((GridData
)getViewer().getControl().getLayoutData()).exclude
= displayProgress
;
1447 getViewer().getControl().setVisible(!displayProgress
);
1449 ((GridData
)treeRuler
.getLayoutData()).exclude
= displayProgress
;
1450 treeRuler
.setVisible(!displayProgress
);
1453 getControl().layout(true, true);
1457 private void compareInputChangedToNull() {
1458 if (!inputChangedTask
.cancel()) {
1460 inputChangedTask
.join();
1461 } catch (InterruptedException e
) {
1462 Thread
.currentThread().interrupt();
1463 Throwables
.propagate(e
);
1467 ResourceSet leftResourceSet
= null;
1468 ResourceSet rightResourceSet
= null;
1469 ResourceSet originResourceSet
= null;
1471 final Comparison comparison
= getCompareConfiguration().getComparison();
1472 if (comparison
!= null) {
1473 Iterator
<Match
> matchIt
= comparison
.getMatches().iterator();
1474 if (comparison
.isThreeWay()) {
1475 while (matchIt
.hasNext() && (leftResourceSet
== null || rightResourceSet
== null
1476 || originResourceSet
== null)) {
1477 Match match
= matchIt
.next();
1478 if (leftResourceSet
== null) {
1479 leftResourceSet
= getResourceSet(match
.getLeft());
1481 if (rightResourceSet
== null) {
1482 rightResourceSet
= getResourceSet(match
.getRight());
1484 if (originResourceSet
== null) {
1485 originResourceSet
= getResourceSet(match
.getOrigin());
1489 while (matchIt
.hasNext() && (leftResourceSet
== null || rightResourceSet
== null)) {
1490 Match match
= matchIt
.next();
1491 if (leftResourceSet
== null) {
1492 leftResourceSet
= getResourceSet(match
.getLeft());
1494 if (rightResourceSet
== null) {
1495 rightResourceSet
= getResourceSet(match
.getRight());
1499 comparison
.eAdapters().clear();
1502 editingDomainChange(getCompareConfiguration().getEditingDomain(), null);
1504 if (resourceSetShouldBeDisposed
) {
1505 final ResourceSet finalLeftResourceSet
= leftResourceSet
;
1506 final ResourceSet finalRightResourceSet
= rightResourceSet
;
1507 final ResourceSet finalOriginResourceSet
= originResourceSet
;
1508 new Job("Resource Disposer") { //$NON-NLS-1$
1510 protected IStatus
run(IProgressMonitor monitor
) {
1511 disposeResourceSet(finalLeftResourceSet
);
1512 disposeResourceSet(finalRightResourceSet
);
1513 disposeResourceSet(finalOriginResourceSet
);
1514 return Status
.OK_STATUS
;
1519 if (getCompareConfiguration() != null) {
1520 getCompareConfiguration().dispose();
1522 getViewer().setInput(null);
1526 * Disposes the {@link ResourceSet}.
1528 * @param resourceSet
1529 * that need to be disposed.
1531 protected void disposeResourceSet(ResourceSet resourceSet
) {
1532 if (resourceSet
instanceof DisposableResourceSet
) {
1533 ((DisposableResourceSet
)resourceSet
).dispose();
1535 unload(resourceSet
);
1540 * Handle the erase item event. When select a difference in the structure merge viewer, highlight required
1541 * differences with a specific color, and highlight unmergeable differences with another color.
1544 * the erase item event.
1546 private void handleEraseItemEvent(Event event
) {
1547 TreeItem item
= (TreeItem
)event
.item
;
1548 EObject dataItem
= EMFCompareStructureMergeViewer
.getDataOfTreeNodeOfAdapter(item
.getData());
1549 if (dataItem
!= null) {
1550 final Set
<Diff
> requires
= dependencyData
.getRequires();
1551 final Set
<Diff
> rejectedDiffs
= dependencyData
.getRejections();
1552 final GC g
= event
.gc
;
1553 if (requires
.contains(dataItem
)) {
1554 paintItemBackground(g
, item
, fColors
.getRequiredFillColor());
1555 } else if (rejectedDiffs
.contains(dataItem
)) {
1556 paintItemBackground(g
, item
, fColors
.getUnmergeableFillColor());
1562 * Paint the background of the given item with the given color.
1565 * the GC associated to the item.
1571 private void paintItemBackground(GC g
, TreeItem item
, Color color
) {
1572 Rectangle itemBounds
= item
.getBounds();
1573 Tree tree
= item
.getParent();
1574 Rectangle areaBounds
= tree
.getClientArea();
1575 g
.setClipping(areaBounds
.x
, itemBounds
.y
, areaBounds
.width
, itemBounds
.height
);
1576 g
.setBackground(color
);
1577 g
.fillRectangle(areaBounds
.x
, itemBounds
.y
, areaBounds
.width
, itemBounds
.height
);
1581 * Returns a problem indication composite for the given diagnostic. If a problem indication composite
1582 * already exists, the existing one is returned. If no composite exists, a new composite is created if the
1583 * severity of the provided diagnostic is anything besides OK. If no composite exists and the severity
1584 * does not warrant the creation of a new composite, this method returns null.
1587 * comparison diagnostic
1588 * @return the existing or a newly created problem indication composite or null if no indication is
1591 private ProblemIndicationComposite
getProblemIndication(Diagnostic diagnostic
) {
1592 Assert
.isNotNull(diagnostic
);
1593 int lastEditorPage
= getPageCount() - 1;
1594 ProblemIndicationComposite problemIndicationComposite
= null;
1595 if (lastEditorPage
>= 0 && getItemControl(lastEditorPage
) instanceof ProblemIndicationComposite
) {
1596 problemIndicationComposite
= ((ProblemIndicationComposite
)getItemControl(lastEditorPage
));
1597 } else if (diagnostic
.getSeverity() != Diagnostic
.OK
&& !getControl().isDisposed()) {
1598 problemIndicationComposite
= new ProblemIndicationComposite(getControl(), SWT
.NONE
);
1599 createItem(++lastEditorPage
, problemIndicationComposite
);
1600 getControl().getItem(lastEditorPage
)
1601 .setText(CommonUIPlugin
.getPlugin().getString("_UI_Problems_label")); //$NON-NLS-1$
1604 return problemIndicationComposite
;
1608 * Updates the problem indication for the provided diagnostic. If everything is {@link Diagnostic#OK} and
1609 * no problem indication is available, this method does nothing. In any other case, the existing or a
1610 * newly created problem indication is updated and automatically revealed if the diagnostics
1611 * {@link Diagnostic#getSeverity() severity} is anything besides {@link Diagnostic#OK} and
1612 * {@link Diagnostic#WARNING}.
1615 * comparison diagnostic
1617 private void updateProblemIndication(Diagnostic diagnostic
) {
1618 ProblemIndicationComposite problemIndicationComposite
= getProblemIndication(diagnostic
);
1619 if (problemIndicationComposite
!= null) {
1620 problemIndicationComposite
.setDiagnostic(diagnostic
);
1621 if (diagnostic
.getSeverity() != Diagnostic
.OK
&& diagnostic
.getSeverity() != Diagnostic
.WARNING
) {
1622 // reveal problem indication composite (last editor page)
1623 int lastEditorPage
= getPageCount() - 1;
1624 setActivePage(lastEditorPage
);
1625 updateLayout(false, true);
1630 private void showTabs() {
1631 if (getPageCount() > 1) {
1632 getControl().getItem(0).setText(
1633 EMFCompareIDEUIMessages
.getString("EMFCompareStructureMergeViewer.tabItem.0.title")); //$NON-NLS-1$
1634 getControl().setTabHeight(SWT
.DEFAULT
);
1635 Point point
= getControl().getSize();
1636 getControl().setSize(point
.x
, point
.y
- 6);
1640 private void hideTabs() {
1641 if (getPageCount() <= 1) {
1642 getControl().getItem(0).setText(""); //$NON-NLS-1$
1643 getControl().setTabHeight(1);
1644 Point point
= getControl().getSize();
1645 getControl().setSize(point
.x
, point
.y
+ 6);
1649 private void setActivePage(int pageIndex
) {
1650 Assert
.isTrue(pageIndex
>= 0 && pageIndex
< getPageCount());
1651 getControl().setSelection(pageIndex
);
1654 private int getPageCount() {
1655 // May not have been created yet, or may have been disposed.
1656 if (getControl() != null && !getControl().isDisposed()) {
1657 return getControl().getItemCount();
1662 private Control
getItemControl(int itemIndex
) {
1663 CTabItem item
= getControl().getItem(itemIndex
);
1665 return item
.getControl();
1670 private static void unload(ResourceSet resourceSet
) {
1671 if (resourceSet
!= null) {
1672 for (Resource resource
: resourceSet
.getResources()) {
1675 resourceSet
.getResources().clear();
1679 private static ResourceSet
getResourceSet(EObject eObject
) {
1680 if (eObject
!= null) {
1681 Resource eResource
= eObject
.eResource();
1682 if (eResource
!= null) {
1683 return eResource
.getResourceSet();
1692 * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
1695 protected void internalRefresh(Object element
) {
1696 // Postpones the refresh if the content provider is in pending mode
1697 getContentProvider().runWhenReady(IN_UI_SYNC
, new Runnable() {
1700 getViewer().refresh();
1703 // Updates dependency data when the viewer has been refreshed and the content provider is ready.
1704 getContentProvider().runWhenReady(IN_UI_SYNC
, new Runnable() {
1706 updateHighlightRelatedChanges(getSelection());
1709 // Needs dependency data however do not need to be run in UI thread
1710 getContentProvider().runWhenReady(IN_UI_ASYNC
, new Runnable() {
1720 * Handles changes to the UI-related preferences in the {@link #preferenceStore}.
1723 * change event for a preference property
1725 protected void handlePreferenceChangedEvent(PropertyChangeEvent event
) {
1726 if (event
.getProperty() == EMFCompareUIPreferences
.EDITOR_TREE_HIGHLIGHT_RELATED_CHANGES
) {
1727 boolean highlightRelatedChanges
= Boolean
.parseBoolean(event
.getNewValue().toString());
1728 if (highlightRelatedChanges
) {
1729 updateHighlightRelatedChanges(getSelection());
1731 clearHighlightRelatedChanges();
1736 private void handleSelectionChangedEvent(SelectionChangedEvent event
) {
1737 if (!Objects
.equal(currentSelection
, event
.getSelection())) {
1738 this.currentSelection
= event
.getSelection();
1739 updateHighlightRelatedChanges(event
.getSelection());
1744 * We need to call redraw() on the tree and the tree ruler because getControl().redraw() doesn't propagate
1745 * the redraw on its sub components under windows platform.
1747 private void internalRedraw() {
1748 Tree tree
= getViewer().getTree();
1749 if (!tree
.isDisposed()) {
1751 if (!treeRuler
.isDisposed()) {
1757 private static class TitleBuilder
{
1759 private final Comparison comparison
;
1761 private final IDifferenceGroupProvider groupProvider
;
1763 private final Predicate
<?
super EObject
> filterPredicate
;
1765 private final Map
<Object
, Boolean
> visited
= Maps
.newHashMap();
1767 private int diffsCount
;
1769 private int visibleDiffsCount
;
1771 private int diffsToMergeCount
;
1773 public TitleBuilder(EMFCompareConfiguration configuration
) {
1774 comparison
= configuration
.getComparison();
1775 groupProvider
= configuration
.getStructureMergeViewerGrouper().getProvider();
1776 filterPredicate
= configuration
.getStructureMergeViewerFilter().getAggregatedPredicate();
1779 void visit(TreeNode node
, boolean parentApplies
) {
1780 boolean applies
= parentApplies
&& filterPredicate
.apply(node
);
1781 EObject data
= node
.getData();
1782 if (data
instanceof Diff
) {
1783 // If we haven't visited it before...
1784 Boolean visitedApplies
= visited
.put(data
, Boolean
.valueOf(applies
));
1785 if (visitedApplies
== null) {
1790 // If it's visible...
1792 // If we didn't visit it as visible before...
1793 if (!Boolean
.TRUE
.equals(visitedApplies
)) {
1794 // Count it as visible.
1795 ++visibleDiffsCount
;
1797 // And if it's not unresolved, count it has needing to be merged.
1798 Diff diff
= (Diff
)data
;
1799 if (diff
.getState() == DifferenceState
.UNRESOLVED
) {
1800 ++diffsToMergeCount
;
1803 } else if (Boolean
.TRUE
.equals(visitedApplies
)) {
1804 // If it was previously counted as visible, but here it's not visible, replace the state
1805 // to indicate it was visible and was counted as visible before.
1806 visited
.put(data
, Boolean
.TRUE
);
1810 for (TreeNode childNode
: node
.getChildren()) {
1811 visit(childNode
, applies
);
1816 public String
toString() {
1817 for (IDifferenceGroup group
: groupProvider
.getGroups(comparison
)) {
1818 for (TreeNode node
: group
.getChildren()) {
1823 @SuppressWarnings("boxing")
1824 String titleArgument
= EMFCompareIDEUIMessages
.getString(
1825 "EMFCompareStructureMergeViewer.titleDesc", //$NON-NLS-1$
1826 diffsToMergeCount
, visibleDiffsCount
, diffsCount
- visibleDiffsCount
);
1827 return titleArgument
;