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

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

see #8465 - use diamond operator where applicable

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