001    /*
002     * Copyright 2003-2004 The Apache Software Foundation
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.events.observable.standard;
017    
018    import java.util.Collection;
019    import java.util.Collections;
020    import java.util.List;
021    import java.util.Map;
022    import java.util.Set;
023    
024    import org.apache.commons.collections.Bag;
025    import org.apache.commons.events.observable.ModificationEvent;
026    import org.apache.commons.events.observable.ModificationEventType;
027    import org.apache.commons.events.observable.ModificationHandler;
028    import org.apache.commons.events.observable.ObservableCollection;
029    
030    /**
031     * Event class that encapsulates the event information for a
032     * standard collection event. Two subclasses are provided, one for
033     * pre and one for post events.
034     * <p>
035     * The information stored in this event is all that is available as
036     * parameters or return values.
037     * In addition, the <code>size</code> method is used on the collection.
038     * All objects used are the real objects from the method calls, not clones.
039     *
040     * @since Commons Events 1.0
041     * @version $Revision: 155443 $ $Date: 2005-02-26 13:19:51 +0000 (Sat, 26 Feb 2005) $
042     * 
043     * @author Stephen Colebourne
044     */
045    public class StandardModificationEvent extends ModificationEvent {
046    
047        /** The size before the event */
048        protected final int preSize;
049        /** The index of the change */
050        protected final int index;
051        /** The object of the change */
052        protected final Object object;
053        /** The number of changes */
054        protected final int repeat;
055        /** The result of the method call */
056        protected final Object previous;
057        /** The view that the event came from, null if none */
058        protected final ObservableCollection view;
059        /** The offset index within the main collection of the view, -1 if none */
060        protected final int viewOffset;
061    
062        // Constructor
063        //-----------------------------------------------------------------------
064        /**
065         * Constructor.
066         * 
067         * @param obsCollection  the event source
068         * @param handler  the handler
069         * @param type  the event type
070         * @param preSize  the size before the change
071         * @param index  the index that changed
072         * @param object  the value that changed
073         * @param repeat  the number of repeats
074         * @param previous  the previous value being removed/replaced
075         * @param view  the view collection, null if event from main collection
076         * @param viewOffset  the offset within the main collection of the view, -1 if unknown
077         */
078        public StandardModificationEvent(
079            final ObservableCollection obsCollection,
080            final ModificationHandler handler,
081            final int type,
082            final int preSize,
083            final int index,
084            final Object object,
085            final int repeat,
086            final Object previous,
087            final ObservableCollection view,
088            final int viewOffset) {
089    
090            super(obsCollection, handler, type);
091            this.preSize = preSize;
092            this.index = index;
093            this.object = object;
094            this.repeat = repeat;
095            this.previous = previous;
096            this.view = view;
097            this.viewOffset = viewOffset;
098        }
099    
100        // Change info
101        //-----------------------------------------------------------------------
102        /**
103         * Gets the index of the change.
104         * <p>
105         * This is <code>-1</code> when not applicable. Typically only used
106         * for {@link java.util.List} events.
107         * 
108         * @return the change index
109         */
110        public int getChangeIndex() {
111            return index;
112        }
113    
114        /**
115         * Gets the object that was added/removed/set.
116         * <p>
117         * This is <code>null</code> when not applicable, such as for clear().
118         * 
119         * @return the changing object
120         */
121        public Object getChangeObject() {
122            return object;
123        }
124    
125        /**
126         * Gets the collection of changed objects.
127         * <p>
128         * For clear, it is an empty list.
129         * For bulk operations, it is the collection.
130         * For non-bulk operations, it is a size one list.
131         * 
132         * @return the changing collection, never null
133         */
134        public Collection getChangeCollection() {
135            if (object == null) {
136                return Collections.EMPTY_LIST;
137            } else if (isType(ModificationEventType.GROUP_BULK)) {
138                if (object instanceof Collection) {
139                    return (Collection) object;
140                } else {
141                    throw new IllegalStateException(
142                        "Bulk operations must involve a Collection, but was " + object.getClass().getName());
143                }
144            } else {
145                return Collections.singletonList(object);
146            }
147        }
148    
149        /**
150         * Gets the number of times the object was added/removed.
151         * <p>
152         * This is normally <code>1</code>, but will be used for 
153         * {@link org.apache.commons.collections.Bag Bag} events.
154         * 
155         * @return the repeat
156         */
157        public int getChangeRepeat() {
158            return repeat;
159        }
160    
161        /**
162         * Gets the previous value that is being replaced or removed.
163         * <p>
164         * This is only returned if the value definitely was previously in the
165         * collection. Bulk operatons will not return this.
166         * 
167         * @return the previous value that was removed/replaced
168         */
169        public Object getPrevious() {
170            return previous;
171        }
172    
173        // Size info
174        //-----------------------------------------------------------------------
175        /**
176         * Gets the size before the change.
177         * 
178         * @return the size before the change
179         */
180        public int getPreSize() {
181            return preSize;
182        }
183    
184        // View info
185        //-----------------------------------------------------------------------
186        /**
187         * Gets the view, <code>null</code> if none.
188         * <p>
189         * A view is a subSet, headSet, tailSet, subList and so on.
190         * 
191         * @return the view
192         */
193        public ObservableCollection getView() {
194            return view;
195        }
196    
197        /**
198         * Checks whether the event originated from a view.
199         * 
200         * @return true if event came from a view
201         */
202        public boolean isView() {
203            return (view != null);
204        }
205    
206        /**
207         * Gets the view offset, <code>-1</code> if no view or unknown offset.
208         * <p>
209         * This refers to the index of the start of the view within the main collection.
210         * 
211         * @return the view offset
212         */
213        public int getViewOffset() {
214            return viewOffset;
215        }
216    
217        // Event type
218        //-----------------------------------------------------------------------
219        /**
220         * Checks to see if the event is an add event (add/addAll).
221         * 
222         * @return true if of the specified type
223         */
224        public boolean isTypeAdd() {
225            return (type & ModificationEventType.GROUP_ADD) > 0;
226        }
227    
228        /**
229         * Checks to see if the event is a remove event (remove/removeAll/retainAll/clear).
230         * 
231         * @return true if of the specified type
232         */
233        public boolean isTypeReduce() {
234            return (type & ModificationEventType.GROUP_REDUCE) > 0;
235        }
236    
237        /**
238         * Checks to see if the event is a change event (set).
239         * 
240         * @return true if of the specified type
241         */
242        public boolean isTypeChange() {
243            return (type & ModificationEventType.GROUP_CHANGE) > 0;
244        }
245    
246        /**
247         * Checks to see if the event is a bulk event (addAll/removeAll/retainAll/clear).
248         * 
249         * @return true if of the specified type
250         */
251        public boolean isTypeBulk() {
252            return (type & ModificationEventType.GROUP_BULK) > 0;
253        }
254    
255        /**
256         * Checks to see if the event is of the specified type.
257         * <p>
258         * This is any combination of constants from {@link ModificationEventType}.
259         * 
260         * @param eventType  an event type constant
261         * @return true if of the specified type
262         */
263        public boolean isType(final int eventType) {
264            return (type & eventType) > 0;
265        }
266    
267        // toString
268        //-----------------------------------------------------------------------
269        /**
270         * Gets a debugging string version of the event.
271         * 
272         * @return a debugging string
273         */
274        public String toString() {
275            StringBuffer buf = new StringBuffer(64);
276            buf.append("ModificationEvent[type=");
277            buf.append(ModificationEventType.toString(type));
278            if (index >= 0) {
279                buf.append(",index=");
280                buf.append(index);
281            }
282            if (type != ModificationEventType.CLEAR) {
283                buf.append(",object=");
284                if (object instanceof List) {
285                    buf.append("List:size:");
286                    buf.append(((List) object).size());
287                } else if (object instanceof Set) {
288                    buf.append("Set:size:");
289                    buf.append(((Set) object).size());
290                } else if (object instanceof Bag) {
291                    buf.append("Bag:size:");
292                    buf.append(((Bag) object).size());
293                } else if (object instanceof Collection) {
294                    buf.append("Collection:size:");
295                    buf.append(((Collection) object).size());
296                } else if (object instanceof Map) {
297                    buf.append("Map:size:");
298                    buf.append(((Map) object).size());
299                } else if (object instanceof Object[]) {
300                    buf.append("Array:size:");
301                    buf.append(((Object[]) object).length);
302                } else if (object == null) {
303                    buf.append("null");
304                } else {
305                    buf.append(object.toString());
306                }
307            }
308            buf.append(']');
309            return buf.toString();
310        }
311    
312    }