1// 2// ======================================================================== 3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4// ------------------------------------------------------------------------ 5// All rights reserved. This program and the accompanying materials 6// are made available under the terms of the Eclipse Public License v1.0 7// and Apache License v2.0 which accompanies this distribution. 8// 9// The Eclipse Public License is available at 10// http://www.eclipse.org/legal/epl-v10.html 11// 12// The Apache License v2.0 is available at 13// http://www.opensource.org/licenses/apache2.0.php 14// 15// You may elect to redistribute this code under either of these licenses. 16// ======================================================================== 17// 18 19package org.eclipse.jetty.util.component; 20import java.lang.ref.WeakReference; 21import java.util.EventListener; 22import java.util.concurrent.CopyOnWriteArrayList; 23 24import org.eclipse.jetty.util.LazyList; 25import org.eclipse.jetty.util.log.Log; 26import org.eclipse.jetty.util.log.Logger; 27 28/* ------------------------------------------------------------ */ 29/** Container. 30 * This class allows a containment events to be generated from update methods. 31 * 32 * The style of usage is: <pre> 33 * public void setFoo(Foo foo) 34 * { 35 * getContainer().update(this,this.foo,foo,"foo"); 36 * this.foo=foo; 37 * } 38 * 39 * public void setBars(Bar[] bars) 40 * { 41 * getContainer().update(this,this.bars,bars,"bar"); 42 * this.bars=bars; 43 * } 44 * </pre> 45 */ 46public class Container 47{ 48 private static final Logger LOG = Log.getLogger(Container.class); 49 private final CopyOnWriteArrayList<Container.Listener> _listeners=new CopyOnWriteArrayList<Container.Listener>(); 50 51 public void addEventListener(Container.Listener listener) 52 { 53 _listeners.add(listener); 54 } 55 56 public void removeEventListener(Container.Listener listener) 57 { 58 _listeners.remove(listener); 59 } 60 61 /* ------------------------------------------------------------ */ 62 /** Update single parent to child relationship. 63 * @param parent The parent of the child. 64 * @param oldChild The previous value of the child. If this is non null and differs from <code>child</code>, then a remove event is generated. 65 * @param child The current child. If this is non null and differs from <code>oldChild</code>, then an add event is generated. 66 * @param relationship The name of the relationship 67 */ 68 public void update(Object parent, Object oldChild, final Object child, String relationship) 69 { 70 if (oldChild!=null && !oldChild.equals(child)) 71 remove(parent,oldChild,relationship); 72 if (child!=null && !child.equals(oldChild)) 73 add(parent,child,relationship); 74 } 75 76 /* ------------------------------------------------------------ */ 77 /** Update single parent to child relationship. 78 * @param parent The parent of the child. 79 * @param oldChild The previous value of the child. If this is non null and differs from <code>child</code>, then a remove event is generated. 80 * @param child The current child. If this is non null and differs from <code>oldChild</code>, then an add event is generated. 81 * @param relationship The name of the relationship 82 * @param addRemove If true add/remove is called for the new/old children as well as the relationships 83 */ 84 public void update(Object parent, Object oldChild, final Object child, String relationship,boolean addRemove) 85 { 86 if (oldChild!=null && !oldChild.equals(child)) 87 { 88 remove(parent,oldChild,relationship); 89 if (addRemove) 90 removeBean(oldChild); 91 } 92 93 if (child!=null && !child.equals(oldChild)) 94 { 95 if (addRemove) 96 addBean(child); 97 add(parent,child,relationship); 98 } 99 } 100 101 /* ------------------------------------------------------------ */ 102 /** Update multiple parent to child relationship. 103 * @param parent The parent of the child. 104 * @param oldChildren The previous array of children. A remove event is generated for any child in this array but not in the <code>children</code> array. 105 * This array is modified and children that remain in the new children array are nulled out of the old children array. 106 * @param children The current array of children. An add event is generated for any child in this array but not in the <code>oldChildren</code> array. 107 * @param relationship The name of the relationship 108 */ 109 public void update(Object parent, Object[] oldChildren, final Object[] children, String relationship) 110 { 111 update(parent,oldChildren,children,relationship,false); 112 } 113 114 /* ------------------------------------------------------------ */ 115 /** Update multiple parent to child relationship. 116 * @param parent The parent of the child. 117 * @param oldChildren The previous array of children. A remove event is generated for any child in this array but not in the <code>children</code> array. 118 * This array is modified and children that remain in the new children array are nulled out of the old children array. 119 * @param children The current array of children. An add event is generated for any child in this array but not in the <code>oldChildren</code> array. 120 * @param relationship The name of the relationship 121 * @param addRemove If true add/remove is called for the new/old children as well as the relationships 122 */ 123 public void update(Object parent, Object[] oldChildren, final Object[] children, String relationship, boolean addRemove) 124 { 125 Object[] newChildren = null; 126 if (children!=null) 127 { 128 newChildren = new Object[children.length]; 129 130 for (int i=children.length;i-->0;) 131 { 132 boolean new_child=true; 133 if (oldChildren!=null) 134 { 135 for (int j=oldChildren.length;j-->0;) 136 { 137 if (children[i]!=null && children[i].equals(oldChildren[j])) 138 { 139 oldChildren[j]=null; 140 new_child=false; 141 } 142 } 143 } 144 if (new_child) 145 newChildren[i]=children[i]; 146 } 147 } 148 149 if (oldChildren!=null) 150 { 151 for (int i=oldChildren.length;i-->0;) 152 { 153 if (oldChildren[i]!=null) 154 { 155 remove(parent,oldChildren[i],relationship); 156 if (addRemove) 157 removeBean(oldChildren[i]); 158 } 159 } 160 } 161 162 if (newChildren!=null) 163 { 164 for (int i=0;i<newChildren.length;i++) 165 if (newChildren[i]!=null) 166 { 167 if (addRemove) 168 addBean(newChildren[i]); 169 add(parent,newChildren[i],relationship); 170 } 171 } 172 } 173 174 /* ------------------------------------------------------------ */ 175 public void addBean(Object obj) 176 { 177 if (_listeners!=null) 178 { 179 for (int i=0; i<LazyList.size(_listeners); i++) 180 { 181 Listener listener=(Listener)LazyList.get(_listeners, i); 182 listener.addBean(obj); 183 } 184 } 185 } 186 187 /* ------------------------------------------------------------ */ 188 public void removeBean(Object obj) 189 { 190 if (_listeners!=null) 191 { 192 for (int i=0; i<LazyList.size(_listeners); i++) 193 ((Listener)LazyList.get(_listeners, i)).removeBean(obj); 194 } 195 } 196 197 /* ------------------------------------------------------------ */ 198 /** Add a parent child relationship 199 * @param parent 200 * @param child 201 * @param relationship 202 */ 203 private void add(Object parent, Object child, String relationship) 204 { 205 if (LOG.isDebugEnabled()) 206 LOG.debug("Container "+parent+" + "+child+" as "+relationship); 207 if (_listeners!=null) 208 { 209 Relationship event=new Relationship(this,parent,child,relationship); 210 for (int i=0; i<LazyList.size(_listeners); i++) 211 ((Listener)LazyList.get(_listeners, i)).add(event); 212 } 213 } 214 215 /* ------------------------------------------------------------ */ 216 /** remove a parent child relationship 217 * @param parent 218 * @param child 219 * @param relationship 220 */ 221 private void remove(Object parent, Object child, String relationship) 222 { 223 if (LOG.isDebugEnabled()) 224 LOG.debug("Container "+parent+" - "+child+" as "+relationship); 225 if (_listeners!=null) 226 { 227 Relationship event=new Relationship(this,parent,child,relationship); 228 for (int i=0; i<LazyList.size(_listeners); i++) 229 ((Listener)LazyList.get(_listeners, i)).remove(event); 230 } 231 } 232 233 /* ------------------------------------------------------------ */ 234 /** A Container event. 235 * @see Listener 236 */ 237 public static class Relationship 238 { 239 private final WeakReference<Object> _parent; 240 private final WeakReference<Object> _child; 241 private String _relationship; 242 private Container _container; 243 244 private Relationship(Container container, Object parent,Object child, String relationship) 245 { 246 _container=container; 247 _parent=new WeakReference<Object>(parent); 248 _child=new WeakReference<Object>(child); 249 _relationship=relationship; 250 } 251 252 public Container getContainer() 253 { 254 return _container; 255 } 256 257 public Object getChild() 258 { 259 return _child.get(); 260 } 261 262 public Object getParent() 263 { 264 return _parent.get(); 265 } 266 267 public String getRelationship() 268 { 269 return _relationship; 270 } 271 272 @Override 273 public String toString() 274 { 275 return _parent+"---"+_relationship+"-->"+_child; 276 } 277 278 @Override 279 public int hashCode() 280 { 281 return _parent.hashCode()+_child.hashCode()+_relationship.hashCode(); 282 } 283 284 @Override 285 public boolean equals(Object o) 286 { 287 if (o==null || !(o instanceof Relationship)) 288 return false; 289 Relationship r = (Relationship)o; 290 return r._parent.get()==_parent.get() && r._child.get()==_child.get() && r._relationship.equals(_relationship); 291 } 292 } 293 294 /* ------------------------------------------------------------ */ 295 /** Listener. 296 * A listener for Container events. 297 */ 298 public interface Listener extends EventListener 299 { 300 public void addBean(Object bean); 301 public void removeBean(Object bean); 302 public void add(Container.Relationship relationship); 303 public void remove(Container.Relationship relationship); 304 } 305} 306