001package jmri.implementation.decorators; 002 003import jmri.NamedBean; 004import jmri.beans.BeanUtil; 005 006import javax.annotation.CheckReturnValue; 007import javax.annotation.Nonnull; 008import javax.annotation.OverridingMethodsMustInvokeSuper; 009import java.beans.PropertyChangeListener; 010import java.beans.PropertyChangeSupport; 011import java.util.ArrayList; 012import java.util.HashMap; 013import java.util.Set; 014 015/** 016 * Abstract base for the NamedBean Decorators. 017 * 018 * @author Bob Jacobsen Copyright (C) 2001 019 * @author Paul Bender Copyright (C) 2020 020 */ 021public abstract class AbstractNamedBeanDecorator implements NamedBean { 022 023 private NamedBean decorated; 024 025 protected AbstractNamedBeanDecorator(NamedBean decorated){ 026 this.decorated = decorated; 027 } 028 029 /** 030 * {@inheritDoc} 031 */ 032 @Override 033 final public String getComment() { 034 return decorated.getComment(); 035 } 036 037 /** 038 * {@inheritDoc} 039 */ 040 @Override 041 final public void setComment(String comment) { 042 decorated.setComment(comment); 043 } 044 045 /** 046 * {@inheritDoc} 047 */ 048 @Override 049 @CheckReturnValue 050 @Nonnull 051 final public String getDisplayName() { 052 return decorated.getDisplayName(); 053 } 054 055 /** 056 * {@inheritDoc} 057 */ 058 @Override 059 @CheckReturnValue 060 @Nonnull 061 final public String getDisplayName(DisplayOptions displayOptions) { 062 return decorated.getDisplayName(displayOptions); 063 } 064 065 // implementing classes will typically have a function/listener to get 066 // updates from the layout, which will then call 067 // public void firePropertyChange(String propertyName, 068 // Object oldValue, 069 // Object newValue) 070 // _once_ if anything has changed state 071 // since we can't do a "super(this)" in the ctor to inherit from PropertyChangeSupport, we'll 072 // reflect to it 073 private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); 074 protected final HashMap<PropertyChangeListener, String> register = new HashMap<>(); 075 protected final HashMap<PropertyChangeListener, String> listenerRefs = new HashMap<>(); 076 077 @Override 078 @OverridingMethodsMustInvokeSuper 079 public synchronized void addPropertyChangeListener(@Nonnull PropertyChangeListener l, 080 String beanRef, String listenerRef) { 081 pcs.addPropertyChangeListener(l); 082 if (beanRef != null) { 083 register.put(l, beanRef); 084 } 085 if (listenerRef != null) { 086 listenerRefs.put(l, listenerRef); 087 } 088 } 089 090 @Override 091 @OverridingMethodsMustInvokeSuper 092 public synchronized void addPropertyChangeListener(@Nonnull String propertyName, 093 @Nonnull PropertyChangeListener l, String beanRef, String listenerRef) { 094 pcs.addPropertyChangeListener(propertyName, l); 095 if (beanRef != null) { 096 register.put(l, beanRef); 097 } 098 if (listenerRef != null) { 099 listenerRefs.put(l, listenerRef); 100 } 101 } 102 103 @Override 104 @OverridingMethodsMustInvokeSuper 105 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { 106 pcs.addPropertyChangeListener(listener); 107 } 108 109 @Override 110 @OverridingMethodsMustInvokeSuper 111 public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { 112 pcs.addPropertyChangeListener(propertyName, listener); 113 } 114 115 @Override 116 @OverridingMethodsMustInvokeSuper 117 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { 118 pcs.removePropertyChangeListener(listener); 119 if (listener != null && !BeanUtil.contains(pcs.getPropertyChangeListeners(), listener)) { 120 register.remove(listener); 121 listenerRefs.remove(listener); 122 } 123 } 124 125 @Override 126 @OverridingMethodsMustInvokeSuper 127 public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { 128 pcs.removePropertyChangeListener(propertyName, listener); 129 if (listener != null && !BeanUtil.contains(pcs.getPropertyChangeListeners(), listener)) { 130 register.remove(listener); 131 listenerRefs.remove(listener); 132 } 133 } 134 135 @Override 136 @Nonnull 137 public synchronized PropertyChangeListener[] getPropertyChangeListenersByReference(@Nonnull String name) { 138 ArrayList<PropertyChangeListener> list = new ArrayList<>(); 139 register.entrySet().forEach((entry) -> { 140 PropertyChangeListener l = entry.getKey(); 141 if (entry.getValue().equals(name)) { 142 list.add(l); 143 } 144 }); 145 return list.toArray(new PropertyChangeListener[list.size()]); 146 } 147 148 /** 149 * Get a meaningful list of places where the bean is in use. 150 * 151 * @return ArrayList of the listeners 152 */ 153 @Override 154 public synchronized ArrayList<String> getListenerRefs() { 155 return new ArrayList<>(listenerRefs.values()); 156 } 157 158 @Override 159 @OverridingMethodsMustInvokeSuper 160 public synchronized void updateListenerRef(PropertyChangeListener l, String newName) { 161 if (listenerRefs.containsKey(l)) { 162 listenerRefs.put(l, newName); 163 } 164 } 165 166 @Override 167 public synchronized String getListenerRef(PropertyChangeListener l) { 168 return listenerRefs.get(l); 169 } 170 171 /** 172 * Get the number of current listeners. 173 * 174 * @return -1 if the information is not available for some reason. 175 */ 176 @Override 177 public synchronized int getNumPropertyChangeListeners() { 178 return pcs.getPropertyChangeListeners().length; 179 } 180 181 @Override 182 @Nonnull 183 public synchronized PropertyChangeListener[] getPropertyChangeListeners() { 184 return pcs.getPropertyChangeListeners(); 185 } 186 187 @Override 188 @Nonnull 189 public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { 190 return pcs.getPropertyChangeListeners(propertyName); 191 } 192 193 /** {@inheritDoc} */ 194 @Override 195 @Nonnull 196 final public String getSystemName() { 197 return decorated.getSystemName(); 198 } 199 200 /** {@inheritDoc} 201 */ 202 @Nonnull 203 @Override 204 final public String toString() { 205 return decorated.getSystemName(); 206 } 207 208 @Override 209 final public String getUserName() { 210 return decorated.getUserName(); 211 } 212 213 @Nonnull 214 @Override 215 public String getBeanType() { 216 return decorated.getBeanType(); 217 } 218 219 @Override 220 @OverridingMethodsMustInvokeSuper 221 public void setUserName(String s) throws BadUserNameException { 222 decorated.setUserName(s); 223 } 224 225 @OverridingMethodsMustInvokeSuper 226 protected void firePropertyChange(String p, Object old, Object n) { 227 pcs.firePropertyChange(p, old, n); 228 } 229 230 @Override 231 @OverridingMethodsMustInvokeSuper 232 public void dispose() { 233 PropertyChangeListener[] listeners = pcs.getPropertyChangeListeners(); 234 for (PropertyChangeListener l : listeners) { 235 pcs.removePropertyChangeListener(l); 236 register.remove(l); 237 listenerRefs.remove(l); 238 } 239 } 240 241 @Override 242 @Nonnull 243 public String describeState(int state) { 244 return decorated.describeState(state); 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override 251 @OverridingMethodsMustInvokeSuper 252 public void setProperty(@Nonnull String key,Object value){ 253 decorated.setProperty(key,value); 254 } 255 256 @Override 257 @OverridingMethodsMustInvokeSuper 258 public Object getProperty(@Nonnull String key) { 259 return decorated.getProperty(key); 260 } 261 262 @Override 263 @OverridingMethodsMustInvokeSuper 264 @Nonnull 265 public Set<String> getPropertyKeys() { 266 return decorated.getPropertyKeys(); 267 } 268 269 @Override 270 @OverridingMethodsMustInvokeSuper 271 public void removeProperty(String key) { 272 decorated.removeProperty(key); 273 } 274 275 @Override 276 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 277 decorated.vetoableChange(evt); 278 } 279 280 /** 281 * {@inheritDoc} 282 * <p> 283 * This implementation tests that 284 * {@link NamedBean#getSystemName()} 285 * is equal for this and obj. 286 * 287 * @param obj the reference object with which to compare. 288 * @return {@code true} if this object is the same as the obj argument; 289 * {@code false} otherwise. 290 */ 291 @Override 292 public boolean equals(Object obj) { 293 if (obj == this) return true; // for efficiency 294 if (obj == null) return false; // by contract 295 296 if (obj instanceof AbstractNamedBeanDecorator) { // NamedBeans are not equal to things of other types 297 AbstractNamedBeanDecorator b = (AbstractNamedBeanDecorator) obj; 298 return this.getSystemName().equals(b.getSystemName()); 299 } 300 301 if(this.decorated.equals(obj)){ 302 // this isn't the same object, but it is decorating the object 303 return true; 304 } 305 306 return false; 307 } 308 309 /** 310 * {@inheritDoc} 311 */ 312 @Override 313 public int hashCode() { 314 return this.getSystemName().hashCode(); 315 } 316 317 /** 318 * {@inheritDoc} 319 */ 320 @CheckReturnValue 321 @Override 322 public int compareSystemNameSuffix(@Nonnull String suffix1, @Nonnull String suffix2, @Nonnull NamedBean n) { 323 return decorated.compareSystemNameSuffix(suffix1,suffix2,n); 324 } 325 326}