1 19 20 package org.netbeans.modules.options; 21 22 import java.beans.PropertyChangeListener ; 23 import java.lang.ref.Reference ; 24 import java.lang.ref.WeakReference ; 25 import java.util.ArrayList ; 26 import java.util.Arrays ; 27 import java.util.Collection ; 28 import java.util.Collections ; 29 import java.util.Iterator ; 30 import java.util.LinkedHashMap ; 31 import java.util.List ; 32 import java.util.Map ; 33 import java.util.Set ; 34 import java.util.concurrent.SynchronousQueue ; 35 import javax.swing.Icon ; 36 import javax.swing.JComponent ; 37 import org.netbeans.spi.options.OptionsCategory; 38 import org.netbeans.spi.options.OptionsPanelController; 39 import org.openide.filesystems.FileObject; 40 import org.openide.filesystems.Repository; 41 import org.openide.loaders.DataFolder; 42 import org.openide.loaders.FolderLookup; 43 import org.openide.util.HelpCtx; 44 import org.openide.util.Lookup; 45 import org.openide.util.LookupEvent; 46 import org.openide.util.LookupListener; 47 import org.openide.util.RequestProcessor; 48 import org.openide.util.lookup.ProxyLookup; 49 50 53 public final class CategoryModel implements LookupListener { 54 private static Reference <CategoryModel> INSTANCE = new WeakReference <CategoryModel>(new CategoryModel()); 55 private final RequestProcessor RP = new RequestProcessor(); 56 private static String currentCategoryID = null; 57 private String highlitedCategoryID = null; 58 private boolean categoriesValid = true; 59 private final Map <String , CategoryModel.Category> id2Category = 60 Collections.synchronizedMap(new LinkedHashMap <String , CategoryModel.Category>()); 61 private MasterLookup masterLookup; 62 private final RequestProcessor.Task masterLookupTask = RP.create(new Runnable () { 63 public void run() { 64 String [] categoryIDs = getCategoryIDs(); 65 List <Lookup> all = new ArrayList <Lookup>(); 66 for (int i = 0; i < categoryIDs.length; i++) { 67 Category item = getCategory(categoryIDs[i]); 68 Lookup lkp = item.getLookup(); 69 assert lkp != null; 70 if (lkp != Lookup.EMPTY) { 71 all.add(lkp); 72 } 73 } 74 getMasterLookup().setLookups(all); 75 } 76 },true); 77 private final RequestProcessor.Task categoryTask = RP.create(new Runnable () { 78 public void run() { 79 Map <String , OptionsCategory> all = loadOptionsCategories(); 80 Map <String , CategoryModel.Category> temp = new LinkedHashMap <String , CategoryModel.Category>(); 81 for (Iterator <Map.Entry <String , OptionsCategory>> it = all.entrySet().iterator(); it.hasNext();) { 82 Map.Entry <String , OptionsCategory> entry = it.next(); 83 OptionsCategory oc = entry.getValue(); 84 String id = entry.getKey(); 85 Category cat = new Category(id, oc); 86 temp.put(cat.getID(), cat); 87 } 88 id2Category.clear(); 89 id2Category.putAll(temp); 90 masterLookupTask.schedule(0); 91 } 92 },true); 93 94 private CategoryModel() { 95 categoryTask.schedule(0); 96 } 97 98 public static CategoryModel getInstance() { 99 CategoryModel retval = (CategoryModel)INSTANCE.get(); 100 if (retval == null) { 101 retval = new CategoryModel(); 102 INSTANCE = new WeakReference <CategoryModel>(retval); 103 } 104 return retval; 105 } 106 107 boolean needsReinit() { 108 synchronized(CategoryModel.class) { 109 return !categoriesValid; 110 } 111 } 112 113 boolean isInitialized() { 114 return categoryTask.isFinished(); 115 } 116 117 boolean isLookupInitialized() { 118 return masterLookupTask.isFinished(); 119 } 120 121 122 void waitForInitialization() { 123 categoryTask.waitFinished(); 124 } 125 126 public String getCurrentCategoryID() { 127 return verifyCategoryID(currentCategoryID); 128 } 129 130 public void setCurrentCategoryID(String categoryID) { 131 currentCategoryID = verifyCategoryID(categoryID); 132 } 133 134 135 String getHighlitedCategoryID() { 136 return verifyCategoryID(highlitedCategoryID); 137 } 138 139 private String verifyCategoryID(String categoryID) { 140 String retval = findCurrentCategoryID(categoryID) != -1 ? categoryID : null; 141 if (retval == null) { 142 String [] categoryIDs = getCategoryIDs(); 143 if (categoryIDs.length > 0) { 144 retval = categoryID = categoryIDs[0]; 145 } 146 } 147 return retval; 148 } 149 150 private int findCurrentCategoryID(String categoryID) { 151 return categoryID == null ? -1 : Arrays.asList(getCategoryIDs()).indexOf(categoryID); 152 } 153 154 public String [] getCategoryIDs() { 155 categoryTask.waitFinished(); 156 Set <String > keys = id2Category.keySet(); 157 return keys.toArray(new String [keys.size()]); 158 } 159 160 Category getCurrent() { 161 String categoryID = getCurrentCategoryID(); 162 return (categoryID == null) ? null : getCategory(categoryID); 163 } 164 165 void setCurrent(Category item) { 166 item.setCurrent(); 167 } 168 169 void setHighlited(Category item) { 170 item.setHighlited(); 171 } 172 173 HelpCtx getHelpCtx() { 174 final CategoryModel.Category category = getCurrent(); 175 return (category == null) ? null : category.getHelpCtx(); 176 } 177 178 void update(PropertyChangeListener l, boolean force) { 179 String [] categoryIDs = getCategoryIDs(); 180 for (int i = 0; i < categoryIDs.length; i++) { 181 CategoryModel.Category item = getCategory(categoryIDs[i]); 182 item.update(l, force); 183 } 184 } 185 186 void save() { 187 String [] categoryIDs = getCategoryIDs(); 188 for (int i = 0; i < categoryIDs.length; i++) { 189 CategoryModel.Category item = getCategory(categoryIDs[i]); 190 item.applyChanges(); 191 } 192 } 193 194 void cancel() { 195 String [] categoryIDs = getCategoryIDs(); 196 for (int i = 0; i < categoryIDs.length; i++) { 197 CategoryModel.Category item = getCategory(categoryIDs[i]); 198 item.cancel(); 199 } 200 } 201 202 boolean dataValid() { 203 boolean retval = true; 204 String [] categoryIDs = getCategoryIDs(); 205 for (int i = 0; retval && i < categoryIDs.length; i++) { 206 CategoryModel.Category item = getCategory(categoryIDs[i]); 207 retval = item.isValid(); 208 } 209 return retval; 210 } 211 212 boolean isChanged() { 213 boolean retval = false; 214 String [] categoryIDs = getCategoryIDs(); 215 for (int i = 0; !retval && i < categoryIDs.length; i++) { 216 CategoryModel.Category item = getCategory(categoryIDs[i]); 217 retval = item.isChanged(); 218 } 219 return retval; 220 } 221 222 223 void setNextCategoryAsCurrent() { 224 int idx = findCurrentCategoryID(getCurrentCategoryID()); 225 String [] categoryIDs = getCategoryIDs(); 226 if (idx >= 0 && idx+1 < categoryIDs.length) { 227 currentCategoryID = categoryIDs[idx+1]; 228 } else { 229 currentCategoryID = null; 230 } 231 } 232 233 void setPreviousCategoryAsCurrent() { 234 int idx = findCurrentCategoryID(getCurrentCategoryID()); 235 String [] categoryIDs = getCategoryIDs(); 236 if (idx >= 0 && idx < categoryIDs.length && categoryIDs.length > 0) { 237 if (idx-1 >= 0) { 238 currentCategoryID = categoryIDs[idx-1]; 239 } else { 240 currentCategoryID = categoryIDs[categoryIDs.length-1]; 241 } 242 } else { 243 currentCategoryID = null; 244 } 245 } 246 247 248 Category getCategory(String categoryID) { 249 categoryTask.waitFinished(); 250 return (Category)id2Category.get(categoryID); 251 } 252 253 private MasterLookup getMasterLookup() { 254 if (masterLookup == null) { 255 masterLookup = new MasterLookup(); 256 } 257 return masterLookup; 258 } 259 260 private Map <String , OptionsCategory> loadOptionsCategories() { 261 FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource("OptionsDialog"); if (fo != null) { 263 Lookup lookup = new FolderLookup(DataFolder.findFolder(fo),null).getLookup(); Lookup.Result<OptionsCategory> result = lookup.lookup(new Lookup.Template<OptionsCategory>(OptionsCategory.class)); 265 result.addLookupListener(this); 266 Map <String , OptionsCategory> m = new LinkedHashMap <String , OptionsCategory>(); 267 for (Iterator <? extends Lookup.Item<OptionsCategory>> it = result.allItems().iterator(); it.hasNext();) { 268 Lookup.Item<OptionsCategory> item = it.next(); 269 m.put(item.getId(), item.getInstance()); 270 } 271 return Collections.unmodifiableMap(m); 272 } 273 return Collections.<String , OptionsCategory>emptyMap(); 274 } 275 276 public void resultChanged(LookupEvent ev) { 277 synchronized(CategoryModel.class) { 278 categoriesValid = false; 279 INSTANCE = new WeakReference <CategoryModel>(new CategoryModel()); 280 } 281 } 282 283 final class Category { 284 private OptionsCategory category; 285 private OptionsPanelController controller; 286 private boolean isUpdated; 287 private HelpCtx helpCtx; 288 private JComponent component; 289 private Lookup lookup; 290 private final String id; 291 292 private Category(final String id, final OptionsCategory category) { 293 this.category = category; 294 this.id = id; 295 } 296 297 boolean isCurrent() { 298 return getID().equals(getCurrentCategoryID()); 299 } 300 301 boolean isHighlited() { 302 return getID().equals(getHighlitedCategoryID()); 303 } 304 305 private void setCurrent() { 306 setCurrentCategoryID(getID()); 307 } 308 309 private void setHighlited() { 310 highlitedCategoryID = getID(); 311 } 312 313 public Icon getIcon() { 314 return category.getIcon(); 315 } 316 317 public String getID() { 320 return id; 321 } 322 323 public String getCategoryName() { 324 return category.getCategoryName(); 325 } 326 327 public String getTitle() { 328 return category.getTitle(); 329 } 330 331 private synchronized OptionsPanelController create() { 332 if (controller == null) { 333 controller = category.create(); 334 } 335 return controller; 336 } 337 338 final void update(PropertyChangeListener l, boolean forceUpdate) { 339 if ((!isUpdated && !forceUpdate) || (isUpdated && forceUpdate)) { 340 isUpdated = true; 341 getComponent(); 342 create().update(); 343 if (l != null) { 344 create().addPropertyChangeListener(l); 345 } 346 } 347 } 348 349 private void applyChanges() { 350 if (isUpdated) { 351 create().applyChanges(); 352 } 353 } 354 355 private void cancel() { 356 if (isUpdated) { 357 create().cancel(); 358 } 359 } 360 361 private boolean isValid() { 362 boolean retval = true; 363 if (isUpdated) { 364 retval = create().isValid(); 365 } 366 return retval; 367 } 368 369 private boolean isChanged() { 370 boolean retval = false; 371 if (isUpdated) { 372 retval = create().isChanged(); 373 } 374 return retval; 375 } 376 377 public JComponent getComponent() { 378 if (component == null) { 379 component = create().getComponent(getMasterLookup()); 380 } 381 return component; 382 } 383 384 private HelpCtx getHelpCtx() { 385 if (helpCtx == null && isUpdated) { 386 helpCtx = create().getHelpCtx(); 387 } 388 return helpCtx; 389 } 390 391 392 private Lookup getLookup() { 393 if (lookup == null) { 394 lookup = create().getLookup(); 395 } 396 return lookup; 397 } 398 } 399 400 private class MasterLookup extends ProxyLookup { 401 private void setLookups(List <Lookup> lookups) { 402 setLookups(lookups.toArray(new Lookup[lookups.size()])); 403 } 404 protected void beforeLookup(Lookup.Template template) { 405 super.beforeLookup(template); 406 masterLookupTask.waitFinished(); 407 } 408 } 409 } 410 | Popular Tags |