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

Last change on this file since 2936 was 2936, checked in by jttt, 14 years ago

Fixed #4408 confict manager: do not have to solve tag- and element-conficts when deleting

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