001package jmri.jmrit.catalog; 002 003import java.beans.PropertyChangeListener; 004import java.beans.PropertyChangeSupport; 005import java.util.ArrayList; 006import java.util.HashMap; 007import javax.annotation.CheckReturnValue; 008import javax.annotation.Nonnull; 009import javax.swing.tree.DefaultTreeModel; 010import jmri.CatalogTree; 011import jmri.CatalogTreeNode; 012import jmri.NamedBean; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * TreeModel used by CatalogPanel to create a tree of resources. 018 * 019 * @author Pete Cressman Copyright 2009 020 */ 021public abstract class AbstractCatalogTree extends DefaultTreeModel implements CatalogTree { 022 023 // force changes through setUserName() to ensure rules are applied 024 // as a side effect require reads through getUserName() 025 private String mUserName; 026 // final so does not need to be private to protect against changes 027 protected final String mSystemName; 028 029 public AbstractCatalogTree(String sysname, String username) { 030 super(new CatalogTreeNode(username)); 031 mSystemName = sysname; 032 // use this form to prevent subclass from overriding setUserName 033 // during construction 034 AbstractCatalogTree.this.setUserName(username); 035 } 036 037 public AbstractCatalogTree(String sysname) { 038 this(sysname, "root"); 039 } 040 041 @CheckReturnValue 042 @Override 043 public String getBeanType() { 044 return Bundle.getMessage("BeanNameCatalog"); 045 } 046 047 /** 048 * Recursively add nodes to the tree 049 * 050 * @param pName Name of the resource to be scanned; this is only used for 051 * the human-readable tree 052 * @param pPath Path to this resource, including the pName part 053 * @param pParent Node for the parent of the resource to be scanned, e.g. 054 * where in the tree to insert it. 055 */ 056 @Override 057 public abstract void insertNodes(String pName, String pPath, CatalogTreeNode pParent); 058 059 /** 060 * Starting point to recursively add nodes to the tree by scanning a file 061 * directory 062 * 063 * @param pathToRoot Path to Directory to be scanned 064 */ 065 @Override 066 public void insertNodes(String pathToRoot) { 067 // root is a field in the super class, so use r for root 068 CatalogTreeNode r = getRoot(); 069 log.debug("insertNodes: rootName= {}, pathToRoot= {}", r.getUserObject(), pathToRoot); 070 insertNodes((String) r.getUserObject(), pathToRoot, r); 071 } 072 073 /** 074 * Get the root element of the tree as a jmri.CatalogTreeNode object 075 * (Instead of Object, as parent swing.TreeModel provides). 076 * 077 * @return the root element 078 */ 079 @CheckReturnValue 080 @Override 081 public CatalogTreeNode getRoot() { 082 return (CatalogTreeNode) super.getRoot(); 083 } 084 085 /* 086 * NamedBean implementation (Copied from AbstractNamedBean) ********* 087 */ 088 /** 089 * Get associated comment text. 090 */ 091 @CheckReturnValue 092 @Override 093 public String getComment() { 094 return this.comment; 095 } 096 097 /** 098 * Set associated comment text. 099 * <p> 100 * Comments can be any valid text. 101 * 102 * @param comment Null means no comment associated. 103 */ 104 @Override 105 public void setComment(String comment) { 106 String old = this.comment; 107 this.comment = comment; 108 firePropertyChange("Comment", old, comment); 109 } 110 private String comment; 111 112 // implementing classes will typically have a function/listener to get 113 // updates from the layout, which will then call 114 // public void firePropertyChange(String propertyName, 115 // Object oldValue, 116 // Object newValue) 117 // _once_ if anything has changed state 118 // since we can't do a "super(this)" in the ctor to inherit from PropertyChangeSupport, we'll 119 // reflect to it 120 java.beans.PropertyChangeSupport pcs = new PropertyChangeSupport(this); 121 122 @Override 123 public synchronized void addPropertyChangeListener(PropertyChangeListener l) { 124 pcs.addPropertyChangeListener(l); 125 } 126 127 @Override 128 public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { 129 pcs.addPropertyChangeListener(propertyName, listener); 130 } 131 132 @Override 133 public synchronized void removePropertyChangeListener(PropertyChangeListener l) { 134 pcs.removePropertyChangeListener(l); 135 } 136 137 @Override 138 public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { 139 pcs.removePropertyChangeListener(propertyName, listener); 140 } 141 142 @Override 143 public synchronized PropertyChangeListener[] getPropertyChangeListeners() { 144 return pcs.getPropertyChangeListeners(); 145 } 146 147 @Override 148 public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { 149 return pcs.getPropertyChangeListeners(propertyName); 150 } 151 152 /** 153 * Number of current listeners. May return -1 if the information is not 154 * available for some reason. 155 */ 156 @CheckReturnValue 157 @Override 158 public synchronized int getNumPropertyChangeListeners() { 159 return pcs.getPropertyChangeListeners().length; 160 } 161 162 HashMap<PropertyChangeListener, String> register = new HashMap<>(); 163 HashMap<PropertyChangeListener, String> listenerRefs = new HashMap<>(); 164 165 @Override 166 public synchronized void addPropertyChangeListener(PropertyChangeListener l, String beanRef, String listenerRef) { 167 pcs.addPropertyChangeListener(l); 168 if (beanRef != null) { 169 register.put(l, beanRef); 170 } 171 if (listenerRef != null) { 172 listenerRefs.put(l, listenerRef); 173 } 174 } 175 176 @Override 177 public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener, String beanRef, String listenerRef) { 178 pcs.addPropertyChangeListener(propertyName, listener); 179 if (beanRef != null) { 180 register.put(listener, beanRef); 181 } 182 if (listenerRef != null) { 183 listenerRefs.put(listener, listenerRef); 184 } 185 } 186 187 @CheckReturnValue 188 @Override 189 public synchronized PropertyChangeListener[] getPropertyChangeListenersByReference(String name) { 190 ArrayList<PropertyChangeListener> list = new ArrayList<>(); 191 register.keySet().stream().filter((l) -> (register.get(l).equals(name))).forEachOrdered((l) -> { 192 list.add(l); 193 }); 194 return list.toArray(new PropertyChangeListener[list.size()]); 195 } 196 197 /* This allows a meaning full list of places where the bean is in use!*/ 198 @CheckReturnValue 199 @Override 200 public synchronized ArrayList<String> getListenerRefs() { 201 return new ArrayList<>(listenerRefs.values()); 202 } 203 204 @Override 205 public synchronized void updateListenerRef(PropertyChangeListener l, String newName) { 206 if (listenerRefs.containsKey(l)) { 207 listenerRefs.put(l, newName); 208 } 209 } 210 211 @CheckReturnValue 212 @Override 213 public synchronized String getListenerRef(java.beans.PropertyChangeListener l) { 214 return listenerRefs.get(l); 215 } 216 217 @CheckReturnValue 218 @Override 219 public String getSystemName() { 220 return mSystemName; 221 } 222 223 @CheckReturnValue 224 @Override 225 public String getUserName() { 226 return mUserName; 227 } 228 229 @Override 230 public void setUserName(String s) { 231 String old = mUserName; 232 mUserName = NamedBean.normalizeUserName(s); 233 firePropertyChange("UserName", old, mUserName); 234 } 235 236 protected void firePropertyChange(String p, Object old, Object n) { 237 pcs.firePropertyChange(p, old, n); 238 } 239 240 @Override 241 public void dispose() { 242 pcs = null; 243 } 244 245 @Override 246 @CheckReturnValue 247 public int getState() { 248 return 0; 249 } 250 251 @Override 252 @CheckReturnValue 253 public String describeState(int state) { 254 switch (state) { 255 case UNKNOWN: 256 return Bundle.getMessage("BeanStateUnknowng"); 257 case INCONSISTENT: 258 return Bundle.getMessage("BeanStateInconsistent"); 259 default: 260 return Bundle.getMessage("BeanStateUnexpected", state); 261 } 262 } 263 264 @Override 265 public void setState(int s) throws jmri.JmriException { 266 } 267 268 public void addDeleteLock(jmri.NamedBean lock) { 269 } 270 271 public void removeDeleteLock(jmri.NamedBean lock) { 272 } 273 274 @CheckReturnValue 275 public boolean isDeleteAllowed() { 276 return true; 277 } 278 279 @Override 280 public void vetoableChange(java.beans.PropertyChangeEvent evt) throws java.beans.PropertyVetoException { 281 } 282 283 /** 284 * {@inheritDoc} 285 * 286 * By default, does an alphanumeric-by-chunks comparison 287 */ 288 @CheckReturnValue 289 @Override 290 public int compareSystemNameSuffix(@Nonnull String suffix1, @Nonnull String suffix2, @Nonnull NamedBean n) { 291 jmri.util.AlphanumComparator ac = new jmri.util.AlphanumComparator(); 292 return ac.compare(suffix1, suffix2); 293 } 294 295 private final static Logger log = LoggerFactory.getLogger(AbstractCatalogTree.class); 296 297}