001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.util.listener; 018 019import java.io.Serializable; 020import java.util.Iterator; 021import java.util.List; 022import java.util.concurrent.CopyOnWriteArrayList; 023 024import org.apache.wicket.util.collections.ReverseListIterator; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028/** 029 * Represents a collection of listeners. Facilitates invocation of events on each listener. 030 * <p> 031 * Listeners will be invoked in the order added to the collection when using 032 * {@link #notify(INotifier)} or in reversed order when using {@link #reversedNotify(INotifier)}. 033 * </p> 034 * 035 * @author ivaynberg (Igor Vaynberg) 036 * @author Jonathan Locke 037 * 038 * @param <T> 039 * type of listeners 040 */ 041public abstract class ListenerCollection<T> implements Serializable, Iterable<T> 042{ 043 private static final long serialVersionUID = 1L; 044 045 private static final Logger logger = LoggerFactory.getLogger(ListenerCollection.class); 046 047 /** list of listeners */ 048 private final List<T> listeners = new CopyOnWriteArrayList<>(); 049 050 /** 051 * Adds a listener to this set of listeners. 052 * 053 * @param listener 054 * The listener to add 055 * @return {@code true} if the listener was added 056 */ 057 public boolean add(final T listener) 058 { 059 if ((listener == null) && !isAllowingNulls()) 060 { 061 return false; 062 } 063 if (!isAllowingDuplicates() && listeners.contains(listener)) 064 { 065 return false; 066 } 067 listeners.add(listener); 068 return true; 069 } 070 071 /** 072 * Notifies each listener in this 073 * 074 * @param notifier 075 * notifier used to notify each listener 076 */ 077 protected void notify(final INotifier<T> notifier) 078 { 079 for (T listener : listeners) 080 { 081 notifier.notify(listener); 082 } 083 } 084 085 /** 086 * Notifies each listener in this set ignoring exceptions. Exceptions will be logged. 087 * 088 * @param notifier 089 * notifier used to notify each listener 090 */ 091 protected void notifyIgnoringExceptions(final INotifier<T> notifier) 092 { 093 for (T listener : listeners) 094 { 095 try 096 { 097 notifier.notify(listener); 098 } 099 catch (Exception e) 100 { 101 logger.error("Error invoking listener: " + listener, e); 102 } 103 } 104 } 105 106 /** 107 * Notifies each listener in this set in reverse order ignoring exceptions 108 * 109 * @param notifier 110 * notifier used to notify each listener 111 */ 112 protected void reversedNotifyIgnoringExceptions(final INotifier<T> notifier) 113 { 114 reversedNotify(new INotifier<T>() 115 { 116 @Override 117 public void notify(T listener) 118 { 119 try 120 { 121 notifier.notify(listener); 122 } 123 catch (Exception e) 124 { 125 logger.error("Error invoking listener: " + listener, e); 126 } 127 128 } 129 130 }); 131 } 132 133 /** 134 * Notifies each listener in this in reversed order 135 * 136 * @param notifier 137 * notifier used to notify each listener 138 */ 139 protected void reversedNotify(final INotifier<T> notifier) 140 { 141 Iterator<T> it = new ReverseListIterator<>(listeners); 142 while (it.hasNext()) 143 { 144 T listener = it.next(); 145 notifier.notify(listener); 146 } 147 } 148 149 /** 150 * Removes a listener from this set. 151 * 152 * @param listener 153 * The listener to remove 154 */ 155 public void remove(final T listener) 156 { 157 listeners.remove(listener); 158 } 159 160 /** 161 * Whether or not added listeners should be checked for duplicates. 162 * 163 * @return {@code true} to ignore duplicates 164 */ 165 protected boolean isAllowingDuplicates() 166 { 167 return true; 168 } 169 170 /** 171 * Whether or not to allow {@code null}s in listener collection. 172 * 173 * @return {@code} true to allow nulls to be added to the collection 174 */ 175 protected boolean isAllowingNulls() 176 { 177 return false; 178 } 179 180 /** 181 * Used to notify a listener. Usually this method simply forwards the {@link #notify(Object)} to 182 * the proper method on the listener. 183 * 184 * @author ivaynberg (Igor Vaynberg) 185 * @param <T> 186 */ 187 protected static interface INotifier<T> 188 { 189 void notify(T listener); 190 } 191 192 /** 193 * Returns an iterator that can iterate the listeners. 194 * 195 * @return an iterator that can iterate the listeners. 196 */ 197 @Override 198 public Iterator<T> iterator() 199 { 200 return listeners.iterator(); 201 } 202}