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; 017 018 import java.util.Set; 019 020 import org.apache.commons.collections.Bag; 021 022 /** 023 * Decorates a <code>Bag</code> implementation to observe modifications. 024 * <p> 025 * Each modifying method call made on this <code>Bag</code> is forwarded to a 026 * {@link ModificationHandler}. 027 * The handler manages the event, notifying listeners and optionally vetoing changes. 028 * The default handler is 029 * {@link org.apache.commons.events.observable.standard.StandardModificationHandler StandardModificationHandler}. 030 * See this class for details of configuration available. 031 * <p> 032 * NOTE: The {@link #uniqueSet()} method returns a <code>Set</code> that is 033 * NOT observed. This is because the set should be unmodifiable. 034 * 035 * @since Commons Events 1.0 036 * @version $Revision: 155443 $ $Date: 2005-02-26 13:19:51 +0000 (Sat, 26 Feb 2005) $ 037 * 038 * @author Stephen Colebourne 039 */ 040 public class ObservableBag extends ObservableCollection implements Bag { 041 042 // Factories 043 //----------------------------------------------------------------------- 044 /** 045 * Factory method to create an observable bag. 046 * <p> 047 * A {@link org.apache.commons.events.observable.standard.StandardModificationHandler} will be created. 048 * This can be accessed by {@link #getHandler()} to add listeners. 049 * 050 * @param bag the bag to decorate, must not be null 051 * @return the observed Bag 052 * @throws IllegalArgumentException if the bag is null 053 */ 054 public static ObservableBag decorate(final Bag bag) { 055 return new ObservableBag(bag, null); 056 } 057 058 /** 059 * Factory method to create an observable bag using a listener or a handler. 060 * <p> 061 * A lot of functionality is available through this method. 062 * If you don't need the extra functionality, simply implement the 063 * {@link org.apache.commons.events.observable.standard.StandardModificationListener} 064 * interface and pass it in as the second parameter. 065 * <p> 066 * Internally, an <code>ObservableBag</code> relies on a {@link ModificationHandler}. 067 * The handler receives all the events and processes them, typically by 068 * calling listeners. Different handler implementations can be plugged in 069 * to provide a flexible event system. 070 * <p> 071 * The handler implementation is determined by the listener parameter via 072 * the registered factories. The listener may be a manually configured 073 * <code>ModificationHandler</code> instance. 074 * <p> 075 * The listener is defined as an Object for maximum flexibility. 076 * It does not have to be a listener in the classic JavaBean sense. 077 * It is entirely up to the factory and handler as to how the parameter 078 * is interpretted. An IllegalArgumentException is thrown if no suitable 079 * handler can be found for this listener. 080 * <p> 081 * A <code>null</code> listener will create a 082 * {@link org.apache.commons.events.observable.standard.StandardModificationHandler}. 083 * 084 * @param bag the bag to decorate, must not be null 085 * @param listener bag listener, may be null 086 * @return the observed bag 087 * @throws IllegalArgumentException if the bag is null 088 * @throws IllegalArgumentException if there is no valid handler for the listener 089 */ 090 public static ObservableBag decorate( 091 final Bag bag, 092 final Object listener) { 093 094 if (bag == null) { 095 throw new IllegalArgumentException("Bag must not be null"); 096 } 097 return new ObservableBag(bag, listener); 098 } 099 100 // Constructors 101 //----------------------------------------------------------------------- 102 /** 103 * Constructor that wraps (not copies). 104 * <p> 105 * The handler implementation is determined by the listener parameter via 106 * the registered factories. The listener may be a manually configured 107 * <code>ModificationHandler</code> instance. 108 * 109 * @param bag the bag to decorate, must not be null 110 * @param listener the listener, may be null 111 * @throws IllegalArgumentException if the bag is null 112 */ 113 protected ObservableBag( 114 final Bag bag, 115 final Object listener) { 116 super(bag, listener); 117 } 118 119 /** 120 * Typecast the collection to a Bag. 121 * 122 * @return the wrapped collection as a Bag 123 */ 124 private Bag getBag() { 125 return (Bag) getCollection(); 126 } 127 128 // Bag API 129 //----------------------------------------------------------------------- 130 public int getCount(Object object) { 131 return getBag().getCount(object); 132 } 133 134 public Set uniqueSet() { 135 return getBag().uniqueSet(); 136 } 137 138 //----------------------------------------------------------------------- 139 public boolean add(Object object) { 140 // override as Bag violates Collection contract 141 boolean result = false; 142 if (handler.preAdd(object)) { 143 result = collection.add(object); 144 handler.postAdd(object, true); // true, as result is misleading 145 } 146 return result; 147 } 148 149 public boolean add(Object object, int nCopies) { 150 boolean result = false; 151 if (handler.preAddNCopies(object, nCopies)) { 152 result = getBag().add(object, nCopies); 153 handler.postAddNCopies(object, nCopies, result); 154 } 155 return result; 156 } 157 158 public boolean remove(Object object, int nCopies) { 159 boolean result = false; 160 if (handler.preRemoveNCopies(object, nCopies)) { 161 result = getBag().remove(object, nCopies); 162 handler.postRemoveNCopies(object, nCopies, result); 163 } 164 return result; 165 } 166 167 }