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

Last change on this file since 3362 was 3362, checked in by stoecker, 14 years ago

fix #5182 - Conflict system simplification - patch by Upliner

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