1 19 20 package org.netbeans.modules.project.ui.groups; 21 22 import java.awt.EventQueue ; 23 import java.io.IOException ; 24 import java.net.URL ; 25 import java.text.Collator ; 26 import java.util.Arrays ; 27 import java.util.Collections ; 28 import java.util.Comparator ; 29 import java.util.HashSet ; 30 import java.util.Set ; 31 import java.util.SortedSet ; 32 import java.util.TreeSet ; 33 import java.util.logging.Level ; 34 import java.util.logging.LogRecord ; 35 import java.util.logging.Logger ; 36 import java.util.prefs.BackingStoreException ; 37 import java.util.prefs.Preferences ; 38 import org.netbeans.api.progress.ProgressHandle; 39 import org.netbeans.api.progress.ProgressHandleFactory; 40 import org.netbeans.api.project.Project; 41 import org.netbeans.api.project.ProjectManager; 42 import org.netbeans.api.project.ProjectUtils; 43 import org.netbeans.api.project.ui.OpenProjects; 44 import org.netbeans.modules.project.ui.ProjectTab; 45 import org.openide.filesystems.FileObject; 46 import org.openide.filesystems.FileStateInvalidException; 47 import org.openide.filesystems.URLMapper; 48 import org.openide.util.Exceptions; 49 import org.openide.util.NbBundle; 50 import org.openide.util.NbPreferences; 51 52 57 public abstract class Group { 58 59 private static final Logger LOG = Logger.getLogger(Group.class.getName()); 60 private static final Logger UILOG = Logger.getLogger("org.netbeans.ui.project.groups"); 61 62 protected static final Preferences NODE = NbPreferences.forModule(Group.class).node("groups"); 63 64 private static final String KEY_ACTIVE = "active"; 66 private static final String KEY_ADVANCED_MODE = "advancedMode"; 68 protected static final String KEY_NAME = "name"; 70 protected static final String KEY_KIND = "kind"; 72 protected static final String KEY_PATH = "path"; 74 protected static final String KEY_MAIN = "main"; 76 private static Group load(String id) { 77 if (id == null) { 78 return null; 79 } 80 String kind = NODE.node(id).get(KEY_KIND, null); 81 if (AdHocGroup.KIND.equals(kind)) { 82 return new AdHocGroup(id); 83 } else if (SubprojectsGroup.KIND.equals(kind)) { 84 return new SubprojectsGroup(id); 85 } else if (DirectoryGroup.KIND.equals(kind)) { 86 return new DirectoryGroup(id); 87 } else { 88 LOG.log(Level.WARNING, "Cannot find project group kind for id={0}", id); 89 return null; 90 } 91 } 92 93 97 public static SortedSet <Group> allGroups() { 98 SortedSet <Group> groups = new TreeSet <Group>(displayNameComparator()); 99 try { 100 for (String id : NODE.childrenNames()) { 101 LOG.log(Level.FINER, "Considering project group id={0}", id); 102 Group g = load(id); 103 if (g != null) { 104 groups.add(g); 105 } 106 } 107 } catch (BackingStoreException x) { 108 Exceptions.printStackTrace(x); 109 } 110 return groups; 111 } 112 113 116 public static Group getActiveGroup() { 117 return load(NODE.get(KEY_ACTIVE, null)); 118 } 119 120 123 public static void setActiveGroup(Group nue) { 124 LOG.log(Level.FINE, "set active group: {0}", nue); 125 if (UILOG.isLoggable(Level.FINER)) { 126 LogRecord rec = new LogRecord (Level.FINER, "Group.UI.setActiveGroup"); 127 rec.setParameters(new Object [] {nue != null ? nue.toString(true) : null}); 128 rec.setResourceBundle(NbBundle.getBundle(Group.class)); 129 rec.setLoggerName(UILOG.getName()); 130 UILOG.log(rec); 131 } 132 Group old = getActiveGroup(); 133 if (old != null) { 134 old.closed(); 135 } 136 if (nue != null) { 137 NODE.put(KEY_ACTIVE, nue.id); 138 } else { 139 NODE.remove(KEY_ACTIVE); 140 } 141 open(nue); 143 } 144 145 public static boolean isAdvancedMode() { 146 return NODE.getBoolean(KEY_ADVANCED_MODE, false); 147 } 148 149 public static void setAdvancedMode(boolean b) { 150 NODE.putBoolean(KEY_ADVANCED_MODE, b); 151 if (UILOG.isLoggable(Level.FINE)) { 152 LogRecord rec = new LogRecord (Level.FINE, "Group.UI.setAdvancedMode"); 153 rec.setParameters(new Object [] {b}); 154 rec.setResourceBundle(NbBundle.getBundle(Group.class)); 155 rec.setLoggerName(UILOG.getName()); 156 UILOG.log(rec); 157 } 158 } 159 160 protected static String sanitizeNameAndUniquifyForId(String name) { 161 String id = name.replaceAll("[^a-zA-Z0-9_.-]+", "_"); 162 Set <String > existing; 163 try { 164 existing = new HashSet <String >(Arrays.asList(NODE.childrenNames())); 165 } catch (BackingStoreException x) { 166 Exceptions.printStackTrace(x); 167 return id; 168 } 169 if (existing.contains(id)) { 170 for (int i = 2; ; i++) { 171 String candidate = id + "_" + i; 172 if (!existing.contains(candidate)) { 173 return candidate; 174 } 175 } 176 } else { 177 return id; 178 } 179 } 180 181 protected final String id; 182 183 protected Group(String id) { 184 this.id = id; 185 assert id.indexOf('/') == -1; 186 } 187 188 protected Preferences prefs() { 189 return NODE.node(id); 190 } 191 192 195 public String getName() { 196 String n = getNameOrNull(); 197 if (n == null) { 198 n = id; 199 } 200 return n; 201 } 202 203 protected String getNameOrNull() { 204 return prefs().get(KEY_NAME, null); 205 } 206 207 210 public void setName(String n) { 211 prefs().put(KEY_NAME, n); 212 if (this.equals(getActiveGroup())) { 213 EventQueue.invokeLater(new Runnable () { 214 public void run() { 215 ProjectTab.findDefault(ProjectTab.ID_LOGICAL).setGroup(Group.this); 216 } 217 }); 218 } 219 } 220 221 protected static Project projectForPath(String path) { 222 if (path != null) { 223 try { 224 FileObject fo = URLMapper.findFileObject(new URL (path)); 225 if (fo != null && fo.isFolder()) { 226 return ProjectManager.getDefault().findProject(fo); 227 } 228 } catch (IOException x) { 229 Exceptions.printStackTrace(x); 230 } 231 } 232 return null; 233 } 234 235 238 public Set <Project> getProjects() { 239 return getProjects(null, 0, 0); 240 } 241 private Set <Project> getProjects(ProgressHandle h, int start, int end) { 242 if (h != null) { 243 h.progress("", start); 244 } 245 Set <Project> projects = new HashSet <Project>(); 246 findProjects(projects, h, start, end); 247 if (h != null) { 248 h.progress("", end); 249 } 250 assert !projects.contains(null) : "Found null in " + projects + " from " + this; 251 return projects; 252 } 253 254 protected abstract void findProjects(Set <Project> projects, ProgressHandle h, int start, int end); 255 256 protected static String progressMessage(Project p) { 257 return NbBundle.getMessage(Group.class, "Group.progress_project", ProjectUtils.getInformation(p).getDisplayName()); 258 } 259 260 263 public Project getMainProject() { 264 return projectForPath(prefs().get(KEY_MAIN, null)); 265 } 266 267 271 public void setMainProject(Project mainProject) throws IllegalArgumentException { 272 LOG.log(Level.FINE, "updating main project for {0} to {1}", new Object [] {id, mainProject}); 273 URL f = null; 274 if (mainProject != null && getProjects().contains(mainProject)) { 275 try { 276 f = mainProject.getProjectDirectory().getURL(); 277 } catch (FileStateInvalidException x) { 278 LOG.log(Level.WARNING, null, x); 279 } 280 } 281 if (f != null) { 282 prefs().put(KEY_MAIN, f.toExternalForm()); 283 } else { 284 if (mainProject != null) { 285 LOG.log(Level.WARNING, "...but not an open project or disk path not found"); 286 } 287 prefs().remove(KEY_MAIN); 288 } 289 } 290 291 294 private static void open(final Group g) { 295 EventQueue.invokeLater(new Runnable () { 296 public void run() { 297 ProjectTab.findDefault(ProjectTab.ID_LOGICAL).setGroup(g); 298 } 299 }); 300 String handleLabel; 301 if (g != null) { 302 handleLabel = NbBundle.getMessage(Group.class, "Group.open_handle", g.getName()); 303 } else { 304 handleLabel = NbBundle.getMessage(Group.class, "Group.close_handle"); 305 } 306 ProgressHandle h = ProgressHandleFactory.createHandle(handleLabel); 307 h.start(200); 308 OpenProjects op = OpenProjects.getDefault(); 309 Set <Project> oldOpen = new HashSet <Project>(Arrays.asList(op.getOpenProjects())); 310 Set <Project> newOpen = g != null ? g.getProjects(h, 10, 100) : Collections.<Project>emptySet(); 311 Set <Project> toClose = new HashSet <Project>(oldOpen); 312 toClose.removeAll(newOpen); 313 Set <Project> toOpen = new HashSet <Project>(newOpen); 314 toOpen.removeAll(oldOpen); 315 assert !toClose.contains(null) : toClose; 316 assert !toOpen.contains(null) : toOpen; 317 h.progress(NbBundle.getMessage(Group.class, "Group.progress_closing", toClose.size()), 120); 318 op.close(toClose.toArray(new Project[toClose.size()])); 319 h.progress(NbBundle.getMessage(Group.class, "Group.progress_opening", toOpen.size()), 140); 320 op.open(toOpen.toArray(new Project[toOpen.size()]), false); 321 if (g != null) { 322 op.setMainProject(g.getMainProject()); 323 } 324 h.finish(); 325 } 326 327 330 protected void closed() { 331 setMainProject(OpenProjects.getDefault().getMainProject()); 332 } 333 334 337 public void destroy() { 338 LOG.log(Level.FINE, "destroying: {0}", id); 339 if (equals(getActiveGroup())) { 340 setActiveGroup(null); 341 } 342 try { 343 Preferences p = prefs(); 344 p.removeNode(); 345 assert !p.nodeExists("") : "failed to destroy " + id; 346 } catch (BackingStoreException x) { 347 Exceptions.printStackTrace(x); 348 } 349 } 350 351 public abstract GroupEditPanel createPropertiesPanel(); 352 353 356 public static Comparator <Group> displayNameComparator() { 357 return new Comparator <Group>() { 358 Collator COLLATOR = Collator.getInstance(); 359 public int compare(Group g1, Group g2) { 360 return COLLATOR.compare(g1.getName(), g2.getName()); 361 } 362 }; 363 } 364 365 public int hashCode() { 366 return id.hashCode(); 367 } 368 369 public boolean equals(Object obj) { 370 return obj instanceof Group && id.equals(((Group) obj).id); 371 } 372 373 public String toString() { 374 return toString(false); 375 } 376 protected String toString(boolean scrubPersonalInfo) { 377 return getClass().getName().replaceFirst("^.+\\.", "") + "[id=" + (scrubPersonalInfo ? "#" + id.hashCode() : id) + ",|projects|=" + getProjects().size() + "]"; 378 } 379 380 385 public boolean isPristine() { 386 return getProjects().equals(new HashSet <Project>(Arrays.asList(OpenProjects.getDefault().getOpenProjects()))); 387 } 388 389 } 390 | Popular Tags |