source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/pair/ConflictResolver.java@ 6890

Last change on this file since 6890 was 6890, checked in by Don-vip, 10 years ago

fix some Sonar issues (Constructor Calls Overridable Method)

  • Property svn:eol-style set to native
File size: 14.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.pair;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.BorderLayout;
8import java.beans.PropertyChangeEvent;
9import java.beans.PropertyChangeListener;
10import java.util.ArrayList;
11import java.util.List;
12
13import javax.swing.ImageIcon;
14import javax.swing.JPanel;
15import javax.swing.JTabbedPane;
16
17import org.openstreetmap.josm.command.Command;
18import org.openstreetmap.josm.command.SequenceCommand;
19import org.openstreetmap.josm.command.conflict.ModifiedConflictResolveCommand;
20import org.openstreetmap.josm.command.conflict.VersionConflictResolveCommand;
21import org.openstreetmap.josm.data.conflict.Conflict;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.Relation;
25import org.openstreetmap.josm.data.osm.Way;
26import org.openstreetmap.josm.gui.conflict.pair.nodes.NodeListMergeModel;
27import org.openstreetmap.josm.gui.conflict.pair.nodes.NodeListMerger;
28import org.openstreetmap.josm.gui.conflict.pair.properties.PropertiesMergeModel;
29import org.openstreetmap.josm.gui.conflict.pair.properties.PropertiesMerger;
30import org.openstreetmap.josm.gui.conflict.pair.relation.RelationMemberListMergeModel;
31import org.openstreetmap.josm.gui.conflict.pair.relation.RelationMemberMerger;
32import org.openstreetmap.josm.gui.conflict.pair.tags.TagMergeModel;
33import org.openstreetmap.josm.gui.conflict.pair.tags.TagMerger;
34import org.openstreetmap.josm.tools.ImageProvider;
35
36/**
37 * An UI component for resolving conflicts between two {@link OsmPrimitive}s.
38 *
39 * This component emits {@link PropertyChangeEvent}s for three properties:
40 * <ul>
41 * <li>{@link #RESOLVED_COMPLETELY_PROP} - new value is <code>true</code>, if the conflict is
42 * completely resolved</li>
43 * <li>{@link #MY_PRIMITIVE_PROP} - new value is the {@link OsmPrimitive} in the role of
44 * my primitive</li>
45 * <li>{@link #THEIR_PRIMITIVE_PROP} - new value is the {@link OsmPrimitive} in the role of
46 * their primitive</li>
47 * </ul>
48 *
49 */
50public class ConflictResolver extends JPanel implements PropertyChangeListener {
51
52 /* -------------------------------------------------------------------------------------- */
53 /* Property names */
54 /* -------------------------------------------------------------------------------------- */
55 /** name of the property indicating whether all conflicts are resolved,
56 * {@link #isResolvedCompletely()}
57 */
58 public static final String RESOLVED_COMPLETELY_PROP = ConflictResolver.class.getName() + ".resolvedCompletely";
59 /**
60 * name of the property for the {@link OsmPrimitive} in the role "my"
61 */
62 public static final String MY_PRIMITIVE_PROP = ConflictResolver.class.getName() + ".myPrimitive";
63
64 /**
65 * name of the property for the {@link OsmPrimitive} in the role "my"
66 */
67 public static final String THEIR_PRIMITIVE_PROP = ConflictResolver.class.getName() + ".theirPrimitive";
68
69 private JTabbedPane tabbedPane = null;
70 private TagMerger tagMerger;
71 private NodeListMerger nodeListMerger;
72 private RelationMemberMerger relationMemberMerger;
73 private PropertiesMerger propertiesMerger;
74 private final List<IConflictResolver> conflictResolvers = new ArrayList<IConflictResolver>();
75 private OsmPrimitive my;
76 private OsmPrimitive their;
77 private Conflict<? extends OsmPrimitive> conflict;
78
79 private ImageIcon mergeComplete;
80 private ImageIcon mergeIncomplete;
81
82 /** indicates whether the current conflict is resolved completely */
83 private boolean resolvedCompletely;
84
85 /**
86 * loads the required icons
87 */
88 protected final void loadIcons() {
89 mergeComplete = ImageProvider.get("dialogs", "valid.png" );
90 mergeIncomplete = ImageProvider.get("dialogs/conflict", "mergeincomplete.png" );
91 }
92
93 /**
94 * builds the UI
95 */
96 protected final void build() {
97 tabbedPane = new JTabbedPane();
98
99 propertiesMerger = new PropertiesMerger();
100 propertiesMerger.setName("panel.propertiesmerger");
101 propertiesMerger.getModel().addPropertyChangeListener(this);
102 tabbedPane.add(tr("Properties"), propertiesMerger);
103
104 tagMerger = new TagMerger();
105 tagMerger.setName("panel.tagmerger");
106 tagMerger.getModel().addPropertyChangeListener(this);
107 tabbedPane.add(tr("Tags"), tagMerger);
108
109 nodeListMerger = new NodeListMerger();
110 nodeListMerger.setName("panel.nodelistmerger");
111 nodeListMerger.getModel().addPropertyChangeListener(this);
112 tabbedPane.add(tr("Nodes"), nodeListMerger);
113
114 relationMemberMerger = new RelationMemberMerger();
115 relationMemberMerger.setName("panel.relationmembermerger");
116 relationMemberMerger.getModel().addPropertyChangeListener(this);
117 tabbedPane.add(tr("Members"), relationMemberMerger);
118
119 setLayout(new BorderLayout());
120 add(tabbedPane, BorderLayout.CENTER);
121
122 conflictResolvers.add(propertiesMerger);
123 conflictResolvers.add(tagMerger);
124 conflictResolvers.add(nodeListMerger);
125 conflictResolvers.add(relationMemberMerger);
126 }
127
128 /**
129 * constructor
130 */
131 public ConflictResolver() {
132 resolvedCompletely = false;
133 build();
134 loadIcons();
135 }
136
137 /**
138 * Sets the {@link OsmPrimitive} in the role "my"
139 *
140 * @param my the primitive in the role "my"
141 */
142 protected void setMy(OsmPrimitive my) {
143 OsmPrimitive old = this.my;
144 this.my = my;
145 if (old != this.my) {
146 firePropertyChange(MY_PRIMITIVE_PROP, old, this.my);
147 }
148 }
149
150 /**
151 * Sets the {@link OsmPrimitive} in the role "their".
152 *
153 * @param their the primitive in the role "their"
154 */
155 protected void setTheir(OsmPrimitive their) {
156 OsmPrimitive old = this.their;
157 this.their = their;
158 if (old != this.their) {
159 firePropertyChange(THEIR_PRIMITIVE_PROP, old, this.their);
160 }
161 }
162
163 /**
164 * handles property change events
165 * @param evt the event
166 * @see TagMergeModel
167 * @see ListMergeModel
168 * @see PropertiesMergeModel
169 */
170 @Override
171 public void propertyChange(PropertyChangeEvent evt) {
172 if (evt.getPropertyName().equals(TagMergeModel.PROP_NUM_UNDECIDED_TAGS)) {
173 int newValue = (Integer)evt.getNewValue();
174 if (newValue == 0) {
175 tabbedPane.setTitleAt(1, tr("Tags"));
176 tabbedPane.setToolTipTextAt(1, tr("No pending tag conflicts to be resolved"));
177 tabbedPane.setIconAt(1, mergeComplete);
178 } else {
179 tabbedPane.setTitleAt(1, trn("Tags({0} conflict)", "Tags({0} conflicts)", newValue, newValue));
180 tabbedPane.setToolTipTextAt(1, trn("{0} pending tag conflict to be resolved", "{0} pending tag conflicts to be resolved", newValue, newValue));
181 tabbedPane.setIconAt(1, mergeIncomplete);
182 }
183 updateResolvedCompletely();
184 } else if (evt.getPropertyName().equals(ListMergeModel.FROZEN_PROP)) {
185 boolean frozen = (Boolean)evt.getNewValue();
186 if (evt.getSource() == nodeListMerger.getModel() && my instanceof Way) {
187 if (frozen) {
188 tabbedPane.setTitleAt(2, tr("Nodes(resolved)"));
189 tabbedPane.setToolTipTextAt(2, tr("Merged node list frozen. No pending conflicts in the node list of this way"));
190 tabbedPane.setIconAt(2, mergeComplete);
191 } else {
192 tabbedPane.setTitleAt(2, tr("Nodes(with conflicts)"));
193 tabbedPane.setToolTipTextAt(2,tr("Pending conflicts in the node list of this way"));
194 tabbedPane.setIconAt(2, mergeIncomplete);
195 }
196 } else if (evt.getSource() == relationMemberMerger.getModel() && my instanceof Relation) {
197 if (frozen) {
198 tabbedPane.setTitleAt(3, tr("Members(resolved)"));
199 tabbedPane.setToolTipTextAt(3, tr("Merged member list frozen. No pending conflicts in the member list of this relation"));
200 tabbedPane.setIconAt(3, mergeComplete);
201 } else {
202 tabbedPane.setTitleAt(3, tr("Members(with conflicts)"));
203 tabbedPane.setToolTipTextAt(3, tr("Pending conflicts in the member list of this relation"));
204 tabbedPane.setIconAt(3, mergeIncomplete);
205 }
206 }
207 updateResolvedCompletely();
208 } else if (evt.getPropertyName().equals(PropertiesMergeModel.RESOLVED_COMPLETELY_PROP)) {
209 boolean resolved = (Boolean)evt.getNewValue();
210 if (resolved) {
211 tabbedPane.setTitleAt(0, tr("Properties"));
212 tabbedPane.setToolTipTextAt(0, tr("No pending property conflicts"));
213 tabbedPane.setIconAt(0, mergeComplete);
214 } else {
215 tabbedPane.setTitleAt(0, tr("Properties(with conflicts)"));
216 tabbedPane.setToolTipTextAt(0, tr("Pending property conflicts to be resolved"));
217 tabbedPane.setIconAt(0, mergeIncomplete);
218 }
219 updateResolvedCompletely();
220 } else if (PropertiesMergeModel.DELETE_PRIMITIVE_PROP.equals(evt.getPropertyName())) {
221 for (IConflictResolver resolver: conflictResolvers) {
222 resolver.deletePrimitive((Boolean) evt.getNewValue());
223 }
224 }
225 }
226
227 /**
228 * populates the conflict resolver with the conflicts between my and their
229 *
230 * @param conflict the conflict data set
231 */
232 public void populate(Conflict<? extends OsmPrimitive> conflict) {
233 setMy(conflict.getMy());
234 setTheir(conflict.getTheir());
235 this.conflict = conflict;
236 propertiesMerger.populate(conflict);
237
238 tabbedPane.setEnabledAt(0, true);
239 tagMerger.populate(conflict);
240 tabbedPane.setEnabledAt(1, true);
241
242 if (my instanceof Node) {
243 tabbedPane.setEnabledAt(2,false);
244 tabbedPane.setEnabledAt(3,false);
245 } else if (my instanceof Way) {
246 nodeListMerger.populate(conflict);
247 tabbedPane.setEnabledAt(2, true);
248 tabbedPane.setEnabledAt(3, false);
249 tabbedPane.setTitleAt(3,tr("Members"));
250 tabbedPane.setIconAt(3, null);
251 } else if (my instanceof Relation) {
252 relationMemberMerger.populate(conflict);
253 tabbedPane.setEnabledAt(2, false);
254 tabbedPane.setTitleAt(2,tr("Nodes"));
255 tabbedPane.setIconAt(2, null);
256 tabbedPane.setEnabledAt(3, true);
257 }
258 updateResolvedCompletely();
259 selectFirstTabWithConflicts();
260 }
261
262 public void selectFirstTabWithConflicts() {
263 for (int i = 0; i < tabbedPane.getTabCount(); i++) {
264 if (tabbedPane.isEnabledAt(i) && mergeIncomplete.equals(tabbedPane.getIconAt(i))) {
265 tabbedPane.setSelectedIndex(i);
266 break;
267 }
268 }
269 }
270
271 /**
272 * Builds the resolution command(s) for the resolved conflicts in this
273 * ConflictResolver
274 *
275 * @return the resolution command
276 */
277 public Command buildResolveCommand() {
278 List<Command> commands = new ArrayList<Command>();
279
280 if (tagMerger.getModel().getNumResolvedConflicts() > 0) {
281 commands.add(tagMerger.getModel().buildResolveCommand(conflict));
282 }
283 commands.addAll(propertiesMerger.getModel().buildResolveCommand(conflict));
284 if (my instanceof Way && nodeListMerger.getModel().isFrozen()) {
285 NodeListMergeModel model = (NodeListMergeModel) nodeListMerger.getModel();
286 commands.add(model.buildResolveCommand(conflict));
287 } else if (my instanceof Relation && relationMemberMerger.getModel().isFrozen()) {
288 RelationMemberListMergeModel model = (RelationMemberListMergeModel) relationMemberMerger.getModel();
289 commands.add(model.buildResolveCommand((Relation) my, (Relation) their));
290 }
291 if (isResolvedCompletely()) {
292 commands.add(new VersionConflictResolveCommand(conflict));
293 commands.add(new ModifiedConflictResolveCommand(conflict));
294 }
295 return new SequenceCommand(tr("Conflict Resolution"), commands);
296 }
297
298 /**
299 * Updates the state of the property {@link #RESOLVED_COMPLETELY_PROP}
300 *
301 */
302 protected void updateResolvedCompletely() {
303 boolean oldValueResolvedCompletely = resolvedCompletely;
304 if (my instanceof Node) {
305 // resolve the version conflict if this is a node and all tag
306 // conflicts have been resolved
307 //
308 this.resolvedCompletely =
309 tagMerger.getModel().isResolvedCompletely()
310 && propertiesMerger.getModel().isResolvedCompletely();
311 } else if (my instanceof Way) {
312 // resolve the version conflict if this is a way, all tag
313 // conflicts have been resolved, and conflicts in the node list
314 // have been resolved
315 //
316 this.resolvedCompletely =
317 tagMerger.getModel().isResolvedCompletely()
318 && propertiesMerger.getModel().isResolvedCompletely()
319 && nodeListMerger.getModel().isFrozen();
320 } else if (my instanceof Relation) {
321 // resolve the version conflict if this is a relation, all tag
322 // conflicts and all conflicts in the member list
323 // have been resolved
324 //
325 this.resolvedCompletely =
326 tagMerger.getModel().isResolvedCompletely()
327 && propertiesMerger.getModel().isResolvedCompletely()
328 && relationMemberMerger.getModel().isFrozen();
329 }
330 if (this.resolvedCompletely != oldValueResolvedCompletely) {
331 firePropertyChange(RESOLVED_COMPLETELY_PROP, oldValueResolvedCompletely, this.resolvedCompletely);
332 }
333 }
334
335 /**
336 * Replies true all differences in this conflicts are resolved
337 *
338 * @return true all differences in this conflicts are resolved
339 */
340 public boolean isResolvedCompletely() {
341 return resolvedCompletely;
342 }
343
344 public void unregisterListeners() {
345 nodeListMerger.unlinkAsListener();
346 relationMemberMerger.unlinkAsListener();
347 }
348}
Note: See TracBrowser for help on using the repository browser.