1 19 package org.netbeans.modules.enode; 20 21 import java.util.ArrayList ; 22 import java.util.Collections ; 23 import java.util.HashMap ; 24 import java.util.HashSet ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.Map ; 28 import java.util.Set ; 29 import javax.swing.Action ; 30 import javax.swing.JComponent ; 31 import javax.swing.JSeparator ; 32 import org.netbeans.api.registry.AttributeEvent; 33 import org.netbeans.api.registry.BindingEvent; 34 import org.netbeans.api.registry.Context; 35 import org.netbeans.api.registry.ContextListener; 36 import org.netbeans.api.registry.SubcontextEvent; 37 import org.openide.ErrorManager; 38 import org.openide.filesystems.FileObject; 39 import org.openide.filesystems.Repository; 40 import org.netbeans.api.enode.ExtensibleNode; 41 import org.openide.util.WeakListeners; 42 43 47 class SubMenuCache { 48 49 private static ErrorManager log = ErrorManager.getDefault().getInstance(SubMenuCache.class.getName()); 50 private static boolean LOGGABLE = log.isLoggable(ErrorManager.INFORMATIONAL); 51 52 53 private static final String SHADOW_EXTENSION = "shadow"; 54 55 56 57 private static SubMenuCache instance; 58 59 65 private Map pathToEntry; 66 67 71 private Listener listener; 72 73 76 private boolean listenersAttached = false; 77 78 84 private Set listenersAttachedTo = new HashSet (); 85 86 87 private SubMenuCache() { 88 buildTheCache(); 89 } 90 91 96 public static SubMenuCache getInstance() { 97 if (instance == null) { 98 instance = new SubMenuCache(); 99 } 100 return instance; 101 } 102 103 106 public CacheEntry getCacheEntry(String originalPath) { 107 return (CacheEntry)getPathToEntryMap().get(originalPath); 108 } 109 110 private FileObject getSubMenusRoot() { 111 return Repository.getDefault().getDefaultFileSystem().findResource( 112 ExtensibleNode.E_NODE_SUBMENUS); 113 } 114 115 private void buildTheCache() { 116 long startTime = System.currentTimeMillis(); 117 FileObject root = getSubMenusRoot(); 118 Context con = Context.getDefault().getSubcontext(root.getPath()); 119 if (con == null) { 120 if (LOGGABLE) log.log("buildTheCache() returning - SubMenu context does not exist."); 121 return; 122 } 123 if (!listenersAttached) { 124 ContextListener l1 = getContextListener(con); 125 con.addContextListener(l1); 126 listenersAttachedTo.add(con); 127 } 128 MenuEntry rootMenu = scanFolder(root, null); 129 getPathToEntryMap().put("", rootMenu); 130 long finishTime = System.currentTimeMillis(); 131 log.log(ErrorManager.USER, "SubMenuCache building has taken " + (finishTime - startTime)); 132 if (LOGGABLE) log.log(this.toString()); 133 } 134 135 139 private MenuEntry scanFolder(FileObject folder, MenuEntry parent) { 140 if (LOGGABLE) log.log("scanFolder(" + folder.getPath() + ") START"); 141 String displayName = folder.getName(); 142 try { 143 displayName = folder.getFileSystem ().getStatus ().annotateName(folder.getName(), Collections.singleton(folder)); 144 } catch (Exception x) { 145 log.notify(ErrorManager.EXCEPTION, x); 146 } 147 MenuEntry result = new MenuEntry(displayName, parent); 148 Context con = Context.getDefault().getSubcontext(folder.getPath()); 150 List orderedNames = con.getOrderedNames(); 151 for (Iterator it = orderedNames.iterator(); it.hasNext();) { 152 String name = (String ) it.next(); 153 if (LOGGABLE) log.log("scanFolder checking " + name); 154 if (name.endsWith("/")) { 155 name = name.substring(0, name.length()-1); 156 } 157 FileObject child = folder.getFileObject(name); 158 if (child == null) { 159 child = folder.getFileObject(name, SHADOW_EXTENSION); 161 } 162 if (child == null) { 163 log.log("child == null: Registry returned an invalid name " + name + " in folder " + folder.getPath()); 164 continue; 165 } 166 if (! child.isValid()) { 167 log.log("!child.isValid(): Registry returned an invalid name " + name + " in folder " + folder.getPath()); 168 continue; 169 } 170 if (child.isData()) { 171 String ext = child.getExt(); 172 if (!SHADOW_EXTENSION.equals(ext)) { 173 log.log("Only .shadows files are allowed in SubMenu folder. Illegal file: " + child.getPath()); 174 continue; 175 } 176 String origPathAttr = (String )child.getAttribute("originalFile"); 177 if (origPathAttr == null) { 178 log.log("Shadow file " + child.getPath() + " is missing the originalFile attribute"); 179 continue; 180 } 181 FileObject origAction = Repository.getDefault().getDefaultFileSystem().findResource(origPathAttr); 182 if (origAction == null) { 183 log.log("originalFile attribute (" + origPathAttr + ") of " + child.getPath() + " does not reference existing action."); 184 continue; 185 } 186 int lastDotIndex = origPathAttr.lastIndexOf('.'); 187 String pathWithoutExt = origPathAttr.substring(0, lastDotIndex); 188 if (LOGGABLE) log.log("adding result " + result + " with path " + pathWithoutExt); 189 ActionEntry ae = new ActionEntry(pathWithoutExt, result); 190 result.addChild(ae); 191 getPathToEntryMap().put(pathWithoutExt, ae); 192 } 193 if (child.isFolder()) { 194 scanFolder(child, result); 195 } 196 } 197 if (parent != null) { 198 parent.addChild(result); 199 } 200 return result; 201 } 202 203 204 private Map getPathToEntryMap() { 205 if (pathToEntry == null) { 206 pathToEntry = new HashMap (); 207 } 208 return pathToEntry; 209 } 210 211 212 public String toString() { 213 String result = "SubMenuCache["; 214 CacheEntry root = getCacheEntry(""); 215 result += convertToString(0, root); 216 return result + "]"; 217 } 218 219 222 private String convertToString(int indent, CacheEntry entry) { 223 StringBuffer sb = new StringBuffer (150); 224 for (int i = 0; i < indent; i++) { 225 sb.append(' '); 226 } 227 sb.append(entry.toString() + "\n"); 228 if (entry instanceof MenuEntry) { 229 MenuEntry me = (MenuEntry)entry; 230 for (int i = 0; i < me.getChildrenCount(); i++) { 231 sb.append(convertToString(indent + 4, me.getChild(i))); 232 } 233 } 234 return sb.toString(); 235 } 236 237 243 private ContextListener getContextListener(Object source) { 244 if (listener == null) { 245 listener = new Listener(); 246 } 247 return (ContextListener)WeakListeners.create(ContextListener.class, listener, source); 248 } 249 250 253 public static class CacheEntry { 254 255 private MenuEntry parent; 256 257 public CacheEntry(MenuEntry parent) { 258 this.parent = parent; 259 } 260 261 262 public int getIndex() { 263 if (parent == null) { 264 return 0; 265 } 266 return parent.getChildIndex(this); 267 } 268 269 public MenuEntry getParent() { 270 return parent; 271 } 272 } 273 274 277 public static class MenuEntry extends CacheEntry { 278 281 private List children; 282 283 284 private String displayName; 285 286 public MenuEntry(String displayName, MenuEntry parent) { 287 super(parent); 288 this.displayName = displayName; 289 children = new ArrayList (); 290 } 291 292 public CacheEntry getChild(int index) { 293 return (CacheEntry) children.get(index); 294 } 295 296 public void addChild(CacheEntry child) { 297 children.add(child); 298 } 299 300 public int getChildIndex(CacheEntry child) { 301 return children.indexOf(child); 302 } 303 304 public int getChildrenCount() { 305 return children.size(); 306 } 307 308 public String getDisplayName() { 309 return displayName; 310 } 311 312 public String toString() { 313 return "MenuEntry[" + displayName +",childrenCount=" + children.size() + "]"; 314 } 315 } 316 317 320 public static class ActionEntry extends CacheEntry { 321 private String originalActionPath; 322 public ActionEntry(String originalActionPath, MenuEntry parent) { 323 super(parent); 324 this.originalActionPath = originalActionPath; 325 } 326 public String toString() { 327 return "ActionEntry["+originalActionPath+",parent="+getParent()+"]"; 328 } 329 335 public Object getActionObject() { 336 int lastSlash = originalActionPath.lastIndexOf('/'); 337 String contextName = originalActionPath.substring(0, lastSlash); 338 Context ctx = Context.getDefault().getSubcontext(contextName); 339 if (ctx == null) { 340 throw new IllegalStateException ("Context " + contextName + " was not found."); 341 } 342 Object obj = ctx.getObject(originalActionPath.substring(lastSlash+1), null); 343 if (obj instanceof Action ) { 344 return obj; 345 } 346 if (obj instanceof JSeparator ) { 347 return obj; 348 } 349 if (obj instanceof JComponent ) { 350 return obj; 351 } 352 throw new IllegalStateException ("Path " + originalActionPath + " cannot be converted to Action.\n" + 353 "Object with name " + originalActionPath.substring(lastSlash+1) + " was not found in " + ctx); 354 } 355 } 356 357 362 private class Listener implements ContextListener { 363 public void attributeChanged(AttributeEvent evt) { 364 if (LOGGABLE) log.log("attributeChanged("+evt+") called on listener from " + SubMenuCache.this); 365 instance = null; 366 } 367 368 public void bindingChanged(BindingEvent evt) { 369 if (LOGGABLE) log.log("bindingChanged("+evt+") called on listener from " + SubMenuCache.this); 370 instance = null; 371 } 372 373 public void subcontextChanged(SubcontextEvent evt) { 374 if (LOGGABLE) log.log("subcontextChanged("+evt+") called on listener from " + SubMenuCache.this); 375 instance = null; 376 } 377 } 378 379 } 380 | Popular Tags |