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