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

Last change on this file since 1926 was 1904, checked in by Gubaer, 15 years ago

fixed #3141: conflict resolution: "apply partial resolution" doesn't work

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