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 }