1 19 20 package org.netbeans.modules.ruby.rubyproject; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.beans.PropertyChangeSupport ; 25 import java.io.File ; 26 import java.io.IOException ; 27 import java.util.Collections ; 28 import javax.swing.Icon ; 29 import javax.swing.ImageIcon ; 30 import org.netbeans.api.java.classpath.ClassPath; 31 import org.netbeans.api.java.classpath.GlobalPathRegistry; 32 import org.netbeans.api.project.Project; 33 import org.netbeans.api.project.ProjectInformation; 34 import org.netbeans.api.project.ProjectManager; 35 import org.netbeans.modules.ruby.rubyproject.SourceRoots; 36 import org.netbeans.modules.ruby.rubyproject.classpath.ClassPathProviderImpl; 37 import org.netbeans.modules.ruby.rubyproject.ui.RubyLogicalViewProvider; 38 import org.netbeans.modules.ruby.rubyproject.ui.customizer.CustomizerProviderImpl; 39 import org.netbeans.modules.ruby.rubyproject.ui.customizer.RubyProjectProperties; 40 import org.netbeans.spi.project.AuxiliaryConfiguration; 41 import org.netbeans.spi.project.SubprojectProvider; 42 import org.netbeans.spi.project.support.LookupProviderSupport; 43 import org.netbeans.modules.ruby.spi.project.support.rake.RakeProjectEvent; 44 import org.netbeans.modules.ruby.spi.project.support.rake.RakeProjectHelper; 45 import org.netbeans.modules.ruby.spi.project.support.rake.RakeProjectListener; 46 import org.netbeans.modules.ruby.spi.project.support.rake.FilterPropertyProvider; 47 import org.netbeans.modules.ruby.spi.project.support.rake.GeneratedFilesHelper; 48 import org.netbeans.modules.ruby.spi.project.support.rake.ProjectXmlSavedHook; 49 import org.netbeans.modules.ruby.spi.project.support.rake.PropertyEvaluator; 50 import org.netbeans.modules.ruby.spi.project.support.rake.PropertyProvider; 51 import org.netbeans.modules.ruby.spi.project.support.rake.PropertyUtils; 52 import org.netbeans.modules.ruby.spi.project.support.rake.ReferenceHelper; 53 import org.netbeans.spi.project.ui.PrivilegedTemplates; 54 import org.netbeans.spi.project.ui.ProjectOpenedHook; 55 import org.netbeans.spi.project.ui.RecommendedTemplates; 56 import org.netbeans.spi.project.ui.support.UILookupMergerSupport; 57 import org.openide.ErrorManager; 58 import org.openide.filesystems.FileObject; 59 import org.openide.filesystems.FileUtil; 60 import org.openide.util.Lookup; 61 import org.openide.util.Mutex; 62 import org.openide.util.Utilities; 63 import org.openide.util.lookup.Lookups; 64 import org.w3c.dom.Element ; 65 import org.w3c.dom.Text ; 66 import org.w3c.dom.Node ; 67 import org.w3c.dom.NodeList ; 68 69 73 public final class RubyProject implements Project, RakeProjectListener { 74 78 public static final String SOURCES_TYPE_RUBY = "ruby"; 80 private static final Icon Ruby_PROJECT_ICON = new ImageIcon (Utilities.loadImage("org/netbeans/modules/ruby/rubyproject/ui/resources/jruby.png")); 82 private final AuxiliaryConfiguration aux; 83 private final RakeProjectHelper helper; 84 private final PropertyEvaluator eval; 85 private final ReferenceHelper refHelper; 86 private final GeneratedFilesHelper genFilesHelper; 87 private final Lookup lookup; 88 private final UpdateHelper updateHelper; 89 private SourceRoots sourceRoots; 91 private SourceRoots testRoots; 92 93 RubyProject(RakeProjectHelper helper) throws IOException { 94 this.helper = helper; 95 eval = createEvaluator(); 96 aux = helper.createAuxiliaryConfiguration(); 97 refHelper = new ReferenceHelper(helper, aux, eval); 98 genFilesHelper = new GeneratedFilesHelper(helper); 99 this.updateHelper = new UpdateHelper (this, this.helper, this.aux, this.genFilesHelper, 100 UpdateHelper.createDefaultNotifier()); 101 102 lookup = createLookup(aux); 103 helper.addRakeProjectListener(this); 104 } 105 106 110 public FileObject getProjectDirectory() { 111 return helper.getProjectDirectory(); 112 } 113 114 public String toString() { 115 return "RubyProject[" + FileUtil.getFileDisplayName(getProjectDirectory()) + "]"; } 117 118 private PropertyEvaluator createEvaluator() { 119 PropertyEvaluator baseEval1 = PropertyUtils.sequentialPropertyEvaluator( 122 helper.getStockPropertyPreprovider(), 123 helper.getPropertyProvider(RubyConfigurationProvider.CONFIG_PROPS_PATH)); 124 PropertyEvaluator baseEval2 = PropertyUtils.sequentialPropertyEvaluator( 125 helper.getStockPropertyPreprovider(), 126 helper.getPropertyProvider(RakeProjectHelper.PRIVATE_PROPERTIES_PATH)); 127 return PropertyUtils.sequentialPropertyEvaluator( 128 helper.getStockPropertyPreprovider(), 129 helper.getPropertyProvider(RubyConfigurationProvider.CONFIG_PROPS_PATH), 130 new ConfigPropertyProvider(baseEval1, "nbproject/private/configs", helper), helper.getPropertyProvider(RakeProjectHelper.PRIVATE_PROPERTIES_PATH), 132 PropertyUtils.userPropertiesProvider(baseEval2, 133 "user.properties.file", FileUtil.toFile(getProjectDirectory())), new ConfigPropertyProvider(baseEval1, "nbproject/configs", helper), helper.getPropertyProvider(RakeProjectHelper.PROJECT_PROPERTIES_PATH)); 136 } 137 private static final class ConfigPropertyProvider extends FilterPropertyProvider implements PropertyChangeListener { 138 private final PropertyEvaluator baseEval; 139 private final String prefix; 140 private final RakeProjectHelper helper; 141 public ConfigPropertyProvider(PropertyEvaluator baseEval, String prefix, RakeProjectHelper helper) { 142 super(computeDelegate(baseEval, prefix, helper)); 143 this.baseEval = baseEval; 144 this.prefix = prefix; 145 this.helper = helper; 146 baseEval.addPropertyChangeListener(this); 147 } 148 public void propertyChange(PropertyChangeEvent ev) { 149 if (RubyConfigurationProvider.PROP_CONFIG.equals(ev.getPropertyName())) { 150 setDelegate(computeDelegate(baseEval, prefix, helper)); 151 } 152 } 153 private static PropertyProvider computeDelegate(PropertyEvaluator baseEval, String prefix, RakeProjectHelper helper) { 154 String config = baseEval.getProperty(RubyConfigurationProvider.PROP_CONFIG); 155 if (config != null) { 156 return helper.getPropertyProvider(prefix + "/" + config + ".properties"); } else { 158 return PropertyUtils.fixedPropertyProvider(Collections.<String ,String >emptyMap()); 159 } 160 } 161 } 162 163 public PropertyEvaluator evaluator() { 164 return eval; 165 } 166 167 ReferenceHelper getReferenceHelper () { 168 return this.refHelper; 169 } 170 171 public UpdateHelper getUpdateHelper() { 172 return this.updateHelper; 173 } 174 175 public Lookup getLookup() { 176 return lookup; 177 } 178 179 public RakeProjectHelper getRakeProjectHelper() { 180 return helper; 181 } 182 183 private Lookup createLookup(AuxiliaryConfiguration aux) { 184 SubprojectProvider spp = refHelper.createSubprojectProvider(); 185 Lookup base = Lookups.fixed(new Object [] { 186 new Info(), 187 aux, 188 helper.createCacheDirectoryProvider(), 189 spp, 190 new RubyActionProvider( this, this.updateHelper ), 191 new RubyLogicalViewProvider(this, this.updateHelper, evaluator(), spp, refHelper), 192 new ClassPathProviderImpl(this.helper, evaluator(), getSourceRoots(),getTestSourceRoots()), new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper), 195 new ProjectXmlSavedHookImpl(), 196 new ProjectOpenedHookImpl(), 197 new RubySources (this.helper, evaluator(), getSourceRoots(), getTestSourceRoots()), 198 new RubySharabilityQuery (this.helper, evaluator(), getSourceRoots(), getTestSourceRoots()), new RubyFileBuiltQuery (this.helper, evaluator(),getSourceRoots(),getTestSourceRoots()), new RecommendedTemplatesImpl (this.updateHelper), 201 this, new RubyProjectOperations(this), 203 new RubyConfigurationProvider(this), 204 UILookupMergerSupport.createPrivilegedTemplatesMerger(), 205 UILookupMergerSupport.createRecommendedTemplatesMerger(), 206 LookupProviderSupport.createSourcesMerger() 207 }); 208 return LookupProviderSupport.createCompositeLookup(base, "Projects/org-netbeans-modules-ruby-rubyproject/Lookup"); } 210 211 public void configurationXmlChanged(RakeProjectEvent ev) { 212 if (ev.getPath().equals(RakeProjectHelper.PROJECT_XML_PATH)) { 213 Info info = (Info)getLookup().lookup(ProjectInformation.class); 215 info.firePropertyChange(ProjectInformation.PROP_NAME); 216 info.firePropertyChange(ProjectInformation.PROP_DISPLAY_NAME); 217 } 218 } 219 220 public void propertiesChanged(RakeProjectEvent ev) { 221 } 223 224 226 230 public synchronized SourceRoots getSourceRoots() { 231 if (this.sourceRoots == null) { this.sourceRoots = new SourceRoots(this.updateHelper, evaluator(), getReferenceHelper(), "source-roots", false, "src.{0}{1}.dir"); } 234 return this.sourceRoots; 235 } 236 237 public synchronized SourceRoots getTestSourceRoots() { 238 if (this.testRoots == null) { this.testRoots = new SourceRoots(this.updateHelper, evaluator(), getReferenceHelper(), "test-roots", true, "test.{0}{1}.dir"); } 241 return this.testRoots; 242 } 243 244 File getTestClassesDirectory() { 245 String testClassesDir = evaluator().getProperty(RubyProjectProperties.BUILD_TEST_CLASSES_DIR); 246 if (testClassesDir == null) { 247 return null; 248 } 249 return helper.resolveFile(testClassesDir); 250 } 251 252 254 public void setName(final String name) { 255 ProjectManager.mutex().writeAccess(new Mutex.Action<Void >() { 256 public Void run() { 257 Element data = helper.getPrimaryConfigurationData(true); 258 NodeList nl = data.getElementsByTagNameNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, "name"); 260 Element nameEl; 261 if (nl.getLength() == 1) { 262 nameEl = (Element ) nl.item(0); 263 NodeList deadKids = nameEl.getChildNodes(); 264 while (deadKids.getLength() > 0) { 265 nameEl.removeChild(deadKids.item(0)); 266 } 267 } else { 268 nameEl = data.getOwnerDocument().createElementNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, "name"); 269 data.insertBefore(nameEl, data.getChildNodes().item(0)); 270 } 271 nameEl.appendChild(data.getOwnerDocument().createTextNode(name)); 272 helper.putPrimaryConfigurationData(data, true); 273 return null; 274 } 275 }); 276 } 277 278 279 280 281 283 private final class Info implements ProjectInformation { 284 285 private final PropertyChangeSupport pcs = new PropertyChangeSupport (this); 286 287 Info() {} 288 289 void firePropertyChange(String prop) { 290 pcs.firePropertyChange(prop, null, null); 291 } 292 293 public String getName() { 294 return PropertyUtils.getUsablePropertyName(getDisplayName()); 295 } 296 297 public String getDisplayName() { 298 return ProjectManager.mutex().readAccess(new Mutex.Action<String >() { 299 public String run() { 300 Element data = updateHelper.getPrimaryConfigurationData(true); 301 NodeList nl = data.getElementsByTagNameNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, "name"); if (nl.getLength() == 1) { 304 nl = nl.item(0).getChildNodes(); 305 if (nl.getLength() == 1 && nl.item(0).getNodeType() == Node.TEXT_NODE) { 306 return ((Text ) nl.item(0)).getNodeValue(); 307 } 308 } 309 return "???"; } 311 }); 312 } 313 314 public Icon getIcon() { 315 return Ruby_PROJECT_ICON; 316 } 317 318 public Project getProject() { 319 return RubyProject.this; 320 } 321 322 public void addPropertyChangeListener(PropertyChangeListener listener) { 323 pcs.addPropertyChangeListener(listener); 324 } 325 326 public void removePropertyChangeListener(PropertyChangeListener listener) { 327 pcs.removePropertyChangeListener(listener); 328 } 329 330 } 331 332 private final class ProjectXmlSavedHookImpl extends ProjectXmlSavedHook { 333 334 ProjectXmlSavedHookImpl() {} 335 336 protected void projectXmlSaved() throws IOException { 337 352 } 353 354 } 355 356 private final class ProjectOpenedHookImpl extends ProjectOpenedHook { 357 358 ProjectOpenedHookImpl() {} 359 360 protected void projectOpened() { 361 379 ClassPathProviderImpl cpProvider = (ClassPathProviderImpl)lookup.lookup(ClassPathProviderImpl.class); 381 GlobalPathRegistry.getDefault().register(ClassPath.BOOT, cpProvider.getProjectClassPaths(ClassPath.BOOT)); 382 GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, cpProvider.getProjectClassPaths(ClassPath.SOURCE)); 383 385 386 389 411 } 412 413 protected void projectClosed() { 414 try { 416 ProjectManager.getDefault().saveProject(RubyProject.this); 417 } catch (IOException e) { 418 ErrorManager.getDefault().notify(e); 419 } 420 421 ClassPathProviderImpl cpProvider = (ClassPathProviderImpl)lookup.lookup(ClassPathProviderImpl.class); 423 GlobalPathRegistry.getDefault().unregister(ClassPath.BOOT, cpProvider.getProjectClassPaths(ClassPath.BOOT)); 424 GlobalPathRegistry.getDefault().unregister(ClassPath.SOURCE, cpProvider.getProjectClassPaths(ClassPath.SOURCE)); 425 427 433 } 434 435 } 436 437 private static final class RecommendedTemplatesImpl implements RecommendedTemplates, PrivilegedTemplates { 438 RecommendedTemplatesImpl (UpdateHelper helper) { 439 this.helper = helper; 440 } 441 442 private UpdateHelper helper; 443 444 446 private static final String [] APPLICATION_TYPES = new String [] { 447 "ruby", "XML", "simple-files" }; 451 452 private static final String [] PRIVILEGED_NAMES = new String [] { 453 "Templates/Ruby/main.rb", "Templates/Ruby/test.rb", "Templates/Ruby/class.rb", "Templates/Ruby/module.rb", "Templates/Ruby/rakefile.rb", "Templates/Ruby/suite.rb", }; 460 461 public String [] getRecommendedTypes() { 462 return APPLICATION_TYPES; 463 } 464 465 public String [] getPrivilegedTemplates() { 466 return PRIVILEGED_NAMES; 467 } 468 469 } 470 } 471 | Popular Tags |