source: josm/trunk/src/org/openstreetmap/josm/gui/conflict/tags/RelationMemberConflictResolverModel.java@ 6770

Last change on this file since 6770 was 5988, checked in by Don-vip, 11 years ago

fix EDT violations observed while merging nodes

  • Property svn:eol-style set to native
File size: 10.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.conflict.tags;
3
4import java.beans.PropertyChangeListener;
5import java.beans.PropertyChangeSupport;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashSet;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.Set;
12
13import javax.swing.table.DefaultTableModel;
14
15import org.openstreetmap.josm.command.ChangeCommand;
16import org.openstreetmap.josm.command.Command;
17import org.openstreetmap.josm.data.osm.OsmPrimitive;
18import org.openstreetmap.josm.data.osm.Relation;
19import org.openstreetmap.josm.data.osm.RelationMember;
20import org.openstreetmap.josm.data.osm.RelationToChildReference;
21import org.openstreetmap.josm.gui.util.GuiHelper;
22
23/**
24 * This model manages a list of conflicting relation members.
25 *
26 * It can be used as {@link javax.swing.table.TableModel}.
27 */
28public class RelationMemberConflictResolverModel extends DefaultTableModel {
29 /** the property name for the number conflicts managed by this model */
30 static public final String NUM_CONFLICTS_PROP = RelationMemberConflictResolverModel.class.getName() + ".numConflicts";
31
32 /** the list of conflict decisions */
33 private List<RelationMemberConflictDecision> decisions;
34 /** the collection of relations for which we manage conflicts */
35 private Collection<Relation> relations;
36 /** the number of conflicts */
37 private int numConflicts;
38 private PropertyChangeSupport support;
39
40 /**
41 * Replies the current number of conflicts
42 *
43 * @return the current number of conflicts
44 */
45 public int getNumConflicts() {
46 return numConflicts;
47 }
48
49 /**
50 * Updates the current number of conflicts from list of decisions and emits
51 * a property change event if necessary.
52 *
53 */
54 protected void updateNumConflicts() {
55 int count = 0;
56 for (RelationMemberConflictDecision decision: decisions) {
57 if (!decision.isDecided()) {
58 count++;
59 }
60 }
61 int oldValue = numConflicts;
62 numConflicts = count;
63 if (numConflicts != oldValue) {
64 support.firePropertyChange(NUM_CONFLICTS_PROP, oldValue, numConflicts);
65 }
66 }
67
68 public void addPropertyChangeListener(PropertyChangeListener l) {
69 support.addPropertyChangeListener(l);
70 }
71
72 public void removePropertyChangeListener(PropertyChangeListener l) {
73 support.removePropertyChangeListener(l);
74 }
75
76 public RelationMemberConflictResolverModel() {
77 decisions = new ArrayList<RelationMemberConflictDecision>();
78 support = new PropertyChangeSupport(this);
79 }
80
81 @Override
82 public int getRowCount() {
83 return getNumDecisions();
84 }
85
86 @Override
87 public Object getValueAt(int row, int column) {
88 if (decisions == null) return null;
89
90 RelationMemberConflictDecision d = decisions.get(row);
91 switch(column) {
92 case 0: /* relation */ return d.getRelation();
93 case 1: /* pos */ return Integer.toString(d.getPos() + 1); // position in "user space" starting at 1
94 case 2: /* role */ return d.getRole();
95 case 3: /* original */ return d.getOriginalPrimitive();
96 case 4: /* decision */ return d.getDecision();
97 }
98 return null;
99 }
100
101 @Override
102 public void setValueAt(Object value, int row, int column) {
103 RelationMemberConflictDecision d = decisions.get(row);
104 switch(column) {
105 case 2: /* role */
106 d.setRole((String)value);
107 break;
108 case 4: /* decision */
109 d.decide((RelationMemberConflictDecisionType)value);
110 refresh();
111 break;
112 }
113 fireTableDataChanged();
114 }
115
116 /**
117 * Populates the model with the members of the relation <code>relation</code>
118 * referring to <code>primitive</code>.
119 *
120 * @param relation the parent relation
121 * @param primitive the child primitive
122 */
123 protected void populate(Relation relation, OsmPrimitive primitive) {
124 for (int i =0; i<relation.getMembersCount();i++) {
125 if (relation.getMember(i).refersTo(primitive)) {
126 decisions.add(new RelationMemberConflictDecision(relation, i));
127 }
128 }
129 }
130
131 /**
132 * Populates the model with the relation members belonging to one of the relations in <code>relations</code>
133 * and referring to one of the primitives in <code>memberPrimitives</code>.
134 *
135 * @param relations the parent relations. Empty list assumed if null.
136 * @param memberPrimitives the child primitives. Empty list assumed if null.
137 */
138 public void populate(Collection<Relation> relations, Collection<? extends OsmPrimitive> memberPrimitives) {
139 decisions.clear();
140 relations = relations == null ? new LinkedList<Relation>() : relations;
141 memberPrimitives = memberPrimitives == null ? new LinkedList<OsmPrimitive>() : memberPrimitives;
142 for (Relation r : relations) {
143 for (OsmPrimitive p: memberPrimitives) {
144 populate(r,p);
145 }
146 }
147 this.relations = relations;
148 refresh();
149 }
150
151 /**
152 * Populates the model with the relation members represented as a collection of
153 * {@link RelationToChildReference}s.
154 *
155 * @param references the references. Empty list assumed if null.
156 */
157 public void populate(Collection<RelationToChildReference> references) {
158 references = references == null ? new LinkedList<RelationToChildReference>() : references;
159 decisions.clear();
160 this.relations = new HashSet<Relation>(references.size());
161 for (RelationToChildReference reference: references) {
162 decisions.add(new RelationMemberConflictDecision(reference.getParent(), reference.getPosition()));
163 relations.add(reference.getParent());
164 }
165 refresh();
166 }
167
168 /**
169 * Replies the decision at position <code>row</code>
170 *
171 * @param row
172 * @return the decision at position <code>row</code>
173 */
174 public RelationMemberConflictDecision getDecision(int row) {
175 return decisions.get(row);
176 }
177
178 /**
179 * Replies the number of decisions managed by this model
180 *
181 * @return the number of decisions managed by this model
182 */
183 public int getNumDecisions() {
184 return decisions == null ? 0 : decisions.size();
185 }
186
187 /**
188 * Refreshes the model state. Invoke this method to trigger necessary change
189 * events after an update of the model data.
190 *
191 */
192 public void refresh() {
193 updateNumConflicts();
194 GuiHelper.runInEDTAndWait(new Runnable() {
195 @Override public void run() {
196 fireTableDataChanged();
197 }
198 });
199 }
200
201 /**
202 * Apply a role to all member managed by this model.
203 *
204 * @param role the role. Empty string assumed if null.
205 */
206 public void applyRole(String role) {
207 role = role == null ? "" : role;
208 for (RelationMemberConflictDecision decision : decisions) {
209 decision.setRole(role);
210 }
211 refresh();
212 }
213
214 protected RelationMemberConflictDecision getDecision(Relation relation, int pos) {
215 for(RelationMemberConflictDecision decision: decisions) {
216 if (decision.matches(relation, pos)) return decision;
217 }
218 return null;
219 }
220
221 protected Command buildResolveCommand(Relation relation, OsmPrimitive newPrimitive) {
222 Relation modifiedRelation = new Relation(relation);
223 modifiedRelation.setMembers(null);
224 boolean isChanged = false;
225 for (int i=0; i < relation.getMembersCount(); i++) {
226 RelationMember rm = relation.getMember(i);
227 RelationMember rmNew;
228 RelationMemberConflictDecision decision = getDecision(relation, i);
229 if (decision == null) {
230 modifiedRelation.addMember(rm);
231 } else {
232 switch(decision.getDecision()) {
233 case KEEP:
234 rmNew = new RelationMember(decision.getRole(),newPrimitive);
235 modifiedRelation.addMember(rmNew);
236 isChanged |= ! rm.equals(rmNew);
237 break;
238 case REMOVE:
239 isChanged = true;
240 // do nothing
241 break;
242 case UNDECIDED:
243 // FIXME: this is an error
244 break;
245 }
246 }
247 }
248 if (isChanged)
249 return new ChangeCommand(relation, modifiedRelation);
250 return null;
251 }
252
253 /**
254 * Builds a collection of commands executing the decisions made in this model.
255 *
256 * @param newPrimitive the primitive which members shall refer to
257 * @return a list of commands
258 */
259 public List<Command> buildResolutionCommands(OsmPrimitive newPrimitive) {
260 List<Command> command = new LinkedList<Command>();
261 for (Relation relation : relations) {
262 Command cmd = buildResolveCommand(relation, newPrimitive);
263 if (cmd != null) {
264 command.add(cmd);
265 }
266 }
267 return command;
268 }
269
270 protected boolean isChanged(Relation relation, OsmPrimitive newPrimitive) {
271 for (int i=0; i < relation.getMembersCount(); i++) {
272 RelationMemberConflictDecision decision = getDecision(relation, i);
273 if (decision == null) {
274 continue;
275 }
276 switch(decision.getDecision()) {
277 case REMOVE: return true;
278 case KEEP:
279 if (!relation.getMember(i).getRole().equals(decision.getRole()))
280 return true;
281 if (relation.getMember(i).getMember() != newPrimitive)
282 return true;
283 case UNDECIDED:
284 // FIXME: handle error
285 }
286 }
287 return false;
288 }
289
290 /**
291 * Replies the set of relations which have to be modified according
292 * to the decisions managed by this model.
293 *
294 * @param newPrimitive the primitive which members shall refer to
295 *
296 * @return the set of relations which have to be modified according
297 * to the decisions managed by this model
298 */
299 public Set<Relation> getModifiedRelations(OsmPrimitive newPrimitive) {
300 HashSet<Relation> ret = new HashSet<Relation>();
301 for (Relation relation: relations) {
302 if (isChanged(relation, newPrimitive)) {
303 ret.add(relation);
304 }
305 }
306 return ret;
307 }
308}
Note: See TracBrowser for help on using the repository browser.