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    }