001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.conflict; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.util.ArrayList; 007import java.util.Collection; 008import java.util.HashSet; 009import java.util.Iterator; 010import java.util.List; 011import java.util.Set; 012import java.util.concurrent.CopyOnWriteArrayList; 013 014import org.openstreetmap.josm.data.osm.OsmPrimitive; 015import org.openstreetmap.josm.tools.CheckParameterUtil; 016 017/** 018 * This is a collection of {@link Conflict}s. This collection is {@link Iterable}, i.e. 019 * it can be used in <code>for</code>-loops as follows: 020 * <pre> 021 * ConflictCollection conflictCollection = .... 022 * 023 * for(Conflict c : conflictCollection) { 024 * // do something 025 * } 026 * </pre> 027 * 028 * This collection emits an event when the content of the collection changes. You can register 029 * and unregister for these events using: 030 * <ul> 031 * <li>{@link #addConflictListener(IConflictListener)}</li> 032 * <li>{@link #removeConflictListener(IConflictListener)}</li> 033 * </ul> 034 */ 035public class ConflictCollection implements Iterable<Conflict<? extends OsmPrimitive>>{ 036 private final List<Conflict<? extends OsmPrimitive>> conflicts; 037 private CopyOnWriteArrayList<IConflictListener> listeners; 038 039 /** 040 * Constructs a new {@code ConflictCollection}. 041 */ 042 public ConflictCollection() { 043 conflicts = new ArrayList<Conflict<? extends OsmPrimitive>>(); 044 listeners = new CopyOnWriteArrayList<IConflictListener>(); 045 } 046 047 /** 048 * Adds the specified conflict listener, if not already present. 049 * @param listener The conflict listener to add 050 */ 051 public void addConflictListener(IConflictListener listener) { 052 if (listener != null) { 053 listeners.addIfAbsent(listener); 054 } 055 } 056 057 /** 058 * Removes the specified conflict listener. 059 * @param listener The conflict listener to remove 060 */ 061 public void removeConflictListener(IConflictListener listener) { 062 listeners.remove(listener); 063 } 064 065 protected void fireConflictAdded() { 066 for (IConflictListener listener : listeners) { 067 listener.onConflictsAdded(this); 068 } 069 } 070 071 protected void fireConflictRemoved() { 072 for (IConflictListener listener : listeners) { 073 listener.onConflictsRemoved(this); 074 } 075 } 076 077 /** 078 * Adds a conflict to the collection 079 * 080 * @param conflict the conflict 081 * @exception IllegalStateException thrown, if this collection already includes a 082 * conflict for conflict.getMy() 083 */ 084 protected void addConflict(Conflict<?> conflict) throws IllegalStateException { 085 if (hasConflictForMy(conflict.getMy())) 086 throw new IllegalStateException(tr("Already registered a conflict for primitive ''{0}''.", conflict.getMy().toString())); 087 if (!conflicts.contains(conflict)) { 088 conflicts.add(conflict); 089 } 090 } 091 092 /** 093 * Adds a conflict to the collection of conflicts. 094 * 095 * @param conflict the conflict to add. Must not be null. 096 * @throws IllegalArgumentException thrown, if conflict is null 097 * @throws IllegalStateException thrown if this collection already includes a conflict for conflict.getMy() 098 * 099 */ 100 public void add(Conflict<?> conflict) throws IllegalStateException { 101 CheckParameterUtil.ensureParameterNotNull(conflict, "conflict"); 102 addConflict(conflict); 103 fireConflictAdded(); 104 } 105 106 /** 107 * Add the conflicts in <code>otherConflicts</code> to this collection of conflicts 108 * 109 * @param otherConflicts the collection of conflicts. Does nothing is conflicts is null. 110 */ 111 public void add(Collection<Conflict<?>> otherConflicts) { 112 if (otherConflicts == null) return; 113 for(Conflict<?> c : otherConflicts) { 114 addConflict(c); 115 } 116 fireConflictAdded(); 117 } 118 119 /** 120 * Adds a conflict for the pair of {@link OsmPrimitive}s given by <code>my</code> and 121 * <code>their</code>. 122 * 123 * @param my my primitive 124 * @param their their primitive 125 */ 126 public void add(OsmPrimitive my, OsmPrimitive their) { 127 addConflict(new Conflict<OsmPrimitive>(my, their)); 128 fireConflictAdded(); 129 } 130 131 /** 132 * removes a conflict from this collection 133 * 134 * @param conflict the conflict 135 */ 136 public void remove(Conflict<?> conflict) { 137 conflicts.remove(conflict); 138 fireConflictRemoved(); 139 } 140 141 /** 142 * removes the conflict registered for {@link OsmPrimitive} <code>my</code> if any 143 * 144 * @param my the primitive 145 */ 146 public void remove(OsmPrimitive my) { 147 Iterator<Conflict<?>> it = iterator(); 148 while(it.hasNext()) { 149 if (it.next().isMatchingMy(my)) { 150 it.remove(); 151 } 152 } 153 fireConflictRemoved(); 154 } 155 156 /** 157 * Replies the conflict for the {@link OsmPrimitive} <code>my</code>, null 158 * if no such conflict exists. 159 * 160 * @param my my primitive 161 * @return the conflict for the {@link OsmPrimitive} <code>my</code>, null 162 * if no such conflict exists. 163 */ 164 public Conflict<?> getConflictForMy(OsmPrimitive my) { 165 for(Conflict<?> c : conflicts) { 166 if (c.isMatchingMy(my)) 167 return c; 168 } 169 return null; 170 } 171 /** 172 * Replies the conflict for the {@link OsmPrimitive} <code>their</code>, null 173 * if no such conflict exists. 174 * 175 * @param their their primitive 176 * @return the conflict for the {@link OsmPrimitive} <code>their</code>, null 177 * if no such conflict exists. 178 */ 179 public Conflict<?> getConflictForTheir(OsmPrimitive their) { 180 for(Conflict<?> c : conflicts) { 181 if (c.isMatchingTheir(their)) 182 return c; 183 } 184 return null; 185 } 186 187 /** 188 * Replies true, if this collection includes a conflict for <code>my</code>. 189 * 190 * @param my my primitive 191 * @return true, if this collection includes a conflict for <code>my</code>; false, otherwise 192 */ 193 public boolean hasConflictForMy(OsmPrimitive my) { 194 return getConflictForMy(my) != null; 195 } 196 197 /** 198 * Replies true, if this collection includes a given conflict 199 * 200 * @param c the conflict 201 * @return true, if this collection includes the conflict; false, otherwise 202 */ 203 public boolean hasConflict(Conflict<?> c) { 204 return hasConflictForMy(c.getMy()); 205 } 206 207 /** 208 * Replies true, if this collection includes a conflict for <code>their</code>. 209 * 210 * @param their their primitive 211 * @return true, if this collection includes a conflict for <code>their</code>; false, otherwise 212 */ 213 public boolean hasConflictForTheir(OsmPrimitive their) { 214 return getConflictForTheir(their) != null; 215 } 216 217 /** 218 * Removes any conflicts for the {@link OsmPrimitive} <code>my</code>. 219 * 220 * @param my the primitive 221 */ 222 public void removeForMy(OsmPrimitive my) { 223 Iterator<Conflict<?>> it = iterator(); 224 while(it.hasNext()) { 225 if (it.next().isMatchingMy(my)) { 226 it.remove(); 227 } 228 } 229 } 230 231 /** 232 * Removes any conflicts for the {@link OsmPrimitive} <code>their</code>. 233 * 234 * @param their the primitive 235 */ 236 public void removeForTheir(OsmPrimitive their) { 237 Iterator<Conflict<?>> it = iterator(); 238 while(it.hasNext()) { 239 if (it.next().isMatchingTheir(their)) { 240 it.remove(); 241 } 242 } 243 } 244 245 /** 246 * Replies the conflicts as list. 247 * 248 * @return the list of conflicts 249 */ 250 public List<Conflict<?>> get() { 251 return conflicts; 252 } 253 254 /** 255 * Replies the size of the collection 256 * 257 * @return the size of the collection 258 */ 259 public int size() { 260 return conflicts.size(); 261 } 262 263 /** 264 * Replies the conflict at position <code>idx</code> 265 * 266 * @param idx the index 267 * @return the conflict at position <code>idx</code> 268 */ 269 public Conflict<?> get(int idx) { 270 return conflicts.get(idx); 271 } 272 273 /** 274 * Replies the iterator for this collection. 275 * 276 * @return the iterator 277 */ 278 @Override 279 public Iterator<Conflict<?>> iterator() { 280 return conflicts.iterator(); 281 } 282 283 public void add(ConflictCollection other) { 284 for (Conflict<?> c : other) { 285 add(c); 286 } 287 } 288 289 /** 290 * Replies the set of {@link OsmPrimitive} which participate in the role 291 * of "my" in the conflicts managed by this collection. 292 * 293 * @return the set of {@link OsmPrimitive} which participate in the role 294 * of "my" in the conflicts managed by this collection. 295 */ 296 public Set<OsmPrimitive> getMyConflictParties() { 297 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>(); 298 for (Conflict<?> c: conflicts) { 299 ret.add(c.getMy()); 300 } 301 return ret; 302 } 303 /** 304 * Replies the set of {@link OsmPrimitive} which participate in the role 305 * of "their" in the conflicts managed by this collection. 306 * 307 * @return the set of {@link OsmPrimitive} which participate in the role 308 * of "their" in the conflicts managed by this collection. 309 */ 310 public Set<OsmPrimitive> getTheirConflictParties() { 311 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>(); 312 for (Conflict<?> c: conflicts) { 313 ret.add(c.getTheir()); 314 } 315 return ret; 316 } 317 318 /** 319 * Replies true if this collection is empty 320 * 321 * @return true, if this collection is empty; false, otherwise 322 */ 323 public boolean isEmpty() { 324 return size() == 0; 325 } 326 327 @Override 328 public String toString() { 329 return conflicts.toString(); 330 } 331}