KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ruby > railsprojects > SourceRoots


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.ruby.railsprojects;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.beans.PropertyChangeSupport JavaDoc;
25 import java.io.File JavaDoc;
26 import java.net.MalformedURLException JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.net.URI JavaDoc;
29 import java.util.Arrays JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.List JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.text.MessageFormat JavaDoc;
37 import org.openide.ErrorManager;
38 import org.openide.filesystems.FileObject;
39 import org.openide.filesystems.FileUtil;
40 import org.openide.util.WeakListeners;
41 import org.openide.util.Mutex;
42 import org.openide.util.NbBundle;
43 import org.w3c.dom.Element JavaDoc;
44 import org.w3c.dom.NodeList JavaDoc;
45 import org.w3c.dom.Document JavaDoc;
46 import org.netbeans.modules.ruby.spi.project.support.rake.RakeProjectHelper;
47 import org.netbeans.modules.ruby.spi.project.support.rake.RakeProjectEvent;
48 import org.netbeans.modules.ruby.spi.project.support.rake.RakeProjectListener;
49 import org.netbeans.modules.ruby.spi.project.support.rake.EditableProperties;
50 import org.netbeans.modules.ruby.spi.project.support.rake.PropertyEvaluator;
51 import org.netbeans.modules.ruby.spi.project.support.rake.ReferenceHelper;
52 import org.netbeans.api.project.ProjectManager;
53
54 /**
55  * This class represents a project source roots. It is used to obtain roots as Ant properties, FileObject's
56  * or URLs.
57  * @author Tomas Zezula
58  */

59 public final class SourceRoots {
60
61     public static final String JavaDoc PROP_ROOT_PROPERTIES = "rootProperties"; //NOI18N
62
public static final String JavaDoc PROP_ROOTS = "roots"; //NOI18N
63

64     public static final String JavaDoc DEFAULT_SOURCE_LABEL = NbBundle.getMessage(SourceRoots.class, "NAME_src.dir");
65     public static final String JavaDoc DEFAULT_TEST_LABEL = NbBundle.getMessage(SourceRoots.class, "NAME_test.src.dir");
66
67     private final UpdateHelper helper;
68     private final PropertyEvaluator evaluator;
69     private final ReferenceHelper refHelper;
70     private final String JavaDoc elementName;
71     private final String JavaDoc newRootNameTemplate;
72     private List JavaDoc<String JavaDoc> sourceRootProperties;
73     private List JavaDoc<String JavaDoc> sourceRootNames;
74     private List JavaDoc<FileObject> sourceRoots;
75     private List JavaDoc<URL JavaDoc> sourceRootURLs;
76     private final PropertyChangeSupport JavaDoc support;
77     private final ProjectMetadataListener listener;
78     private final boolean isTest;
79     private final File JavaDoc projectDir;
80
81     /**
82      * Creates new SourceRoots
83      * @param helper
84      * @param evaluator
85      * @param elementName the name of XML element under which are declared the roots
86      * @param newRootNameTemplate template for new property name of source root
87      */

88     SourceRoots (UpdateHelper helper, PropertyEvaluator evaluator, ReferenceHelper refHelper, String JavaDoc elementName, boolean isTest, String JavaDoc newRootNameTemplate) {
89         assert helper != null && evaluator != null && refHelper != null && elementName != null && newRootNameTemplate != null;
90         this.helper = helper;
91         this.evaluator = evaluator;
92         this.refHelper = refHelper;
93         this.elementName = elementName;
94         this.isTest = isTest;
95         this.newRootNameTemplate = newRootNameTemplate;
96         this.projectDir = FileUtil.toFile(this.helper.getRakeProjectHelper().getProjectDirectory());
97         this.support = new PropertyChangeSupport JavaDoc(this);
98         this.listener = new ProjectMetadataListener();
99         this.evaluator.addPropertyChangeListener (WeakListeners.propertyChange(this.listener,this.evaluator));
100         this.helper.getRakeProjectHelper().addRakeProjectListener ((RakeProjectListener)WeakListeners.create(RakeProjectListener.class, this.listener,this.helper));
101     }
102
103
104     /**
105      * Returns the display names of soruce roots
106      * The returned array has the same length as an array returned by the getRootProperties.
107      * It may contain empty strings but not null.
108      * @return an array of String
109      */

110     public String JavaDoc[] getRootNames () {
111         return ProjectManager.mutex().readAccess(new Mutex.Action<String JavaDoc[]>() {
112             public String JavaDoc[] run() {
113                 // For rails, project roots are hardcoded
114
synchronized (SourceRoots.this) {
115                     if (sourceRootNames == null) {
116                         sourceRootNames = new ArrayList JavaDoc<String JavaDoc>(12);
117                         // Note Keep list in sync with root properties list below (#getRootProperties)
118
sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "app_controllers"));
119                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "app_helpers"));
120                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "app_models"));
121                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "app_views"));
122                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "db"));
123                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "lib"));
124                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "log"));
125                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "config"));
126                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "components"));
127                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "test_unit"));
128                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "test_functional"));
129                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "test_fixtures"));
130                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "test_mocks"));
131                         sourceRootNames.add(NbBundle.getMessage(SourceRoots.class, "test_integration"));
132                     }
133                 }
134                 return sourceRootNames.toArray (new String JavaDoc[sourceRootNames.size()]);
135             }
136         });
137                 
138     }
139
140     /**
141      * Returns names of Ant properties in the project.properties file holding the source roots.
142      * @return an array of String
143      */

144     public String JavaDoc[] getRootProperties () {
145         return ProjectManager.mutex().readAccess(new Mutex.Action<String JavaDoc[]>() {
146             public String JavaDoc[] run() {
147                 synchronized (SourceRoots.this) {
148                     if (sourceRootProperties == null) {
149                         // Note Keep list in sync with property names list above!
150
sourceRootProperties = new ArrayList JavaDoc<String JavaDoc>(12);
151                         sourceRootProperties.add("app/controllers");
152                         sourceRootProperties.add("app/helpers");
153                         sourceRootProperties.add("app/models");
154                         sourceRootProperties.add("app/views");
155                         sourceRootProperties.add("db");
156                         sourceRootProperties.add("lib");
157                         sourceRootProperties.add("log");
158                         sourceRootProperties.add("config");
159                         sourceRootProperties.add("components");
160                         sourceRootProperties.add("test/unit");
161                         sourceRootProperties.add("test/functional");
162                         sourceRootProperties.add("test/fixtures");
163                         sourceRootProperties.add("test/mocks");
164                         sourceRootProperties.add("test/integration");
165                     }
166                 }
167                 return sourceRootProperties.toArray (new String JavaDoc[sourceRootProperties.size()]);
168             }
169         });
170     }
171
172     /**
173      * Returns the source roots
174      * @return an array of FileObject
175      */

176     public FileObject[] getRoots () {
177         return ProjectManager.mutex().readAccess(new Mutex.Action<FileObject[]>() {
178                 public FileObject[] run () {
179                     synchronized (this) {
180                         //Local caching
181
if (sourceRoots == null) {
182                             String JavaDoc[] srcProps = getRootProperties();
183                             List JavaDoc<FileObject> result = new ArrayList JavaDoc<FileObject>();
184                             for (String JavaDoc p : srcProps) {
185                                 FileObject f = helper.getRakeProjectHelper().resolveFileObject(p);
186                                 if (f == null) {
187                                     continue;
188                                 }
189                                 if (FileUtil.isArchiveFile(f)) {
190                                     f = FileUtil.getArchiveRoot(f);
191                                 }
192                                 result.add(f);
193                             }
194                             sourceRoots = Collections.unmodifiableList(result);
195                         }
196                     }
197                     return sourceRoots.toArray(new FileObject[sourceRoots.size()]);
198                 }
199         });
200     }
201
202     /**
203      * Returns the source roots as URLs.
204      * @return an array of URL
205      */

206     public URL JavaDoc[] getRootURLs() {
207         return ProjectManager.mutex().readAccess(new Mutex.Action<URL JavaDoc[]>() {
208             public URL JavaDoc[] run () {
209                 synchronized (this) {
210                     //Local caching
211
if (sourceRootURLs == null) {
212                         String JavaDoc[] srcProps = getRootProperties();
213                         List JavaDoc<URL JavaDoc> result = new ArrayList JavaDoc<URL JavaDoc>();
214                         for (int i = 0; i<srcProps.length; i++) {
215                             String JavaDoc prop = srcProps[i];
216                             if (prop != null) {
217                                 File JavaDoc f = helper.getRakeProjectHelper().resolveFile(prop);
218                                 try {
219                                     URL JavaDoc url = f.toURI().toURL();
220                                     if (!f.exists()) {
221                                         url = new URL JavaDoc(url.toExternalForm() + "/"); // NOI18N
222
}
223                                     result.add(url);
224                                 } catch (MalformedURLException JavaDoc e) {
225                                     ErrorManager.getDefault().notify(e);
226                                 }
227                             }
228                         }
229                         sourceRootURLs = Collections.unmodifiableList(result);
230                     }
231                 }
232                 return sourceRootURLs.toArray(new URL JavaDoc[sourceRootURLs.size()]);
233             }
234         });
235     }
236
237     /**
238      * Adds PropertyChangeListener
239      * @param listener
240      */

241     public void addPropertyChangeListener (PropertyChangeListener JavaDoc listener) {
242         this.support.addPropertyChangeListener (listener);
243     }
244
245     /**
246      * Removes PropertyChangeListener
247      * @param listener
248      */

249     public void removePropertyChangeListener (PropertyChangeListener JavaDoc listener) {
250         this.support.removePropertyChangeListener (listener);
251     }
252
253
254 // /**
255
// * Replaces the current roots by the new ones
256
// * @param roots the URLs of new roots
257
// * @param labels the names of roots
258
// */
259
// public void putRoots (final URL[] roots, final String[] labels) {
260
// ProjectManager.mutex().writeAccess(
261
// new Mutex.Action<Void>() {
262
// public Void run() {
263
// String[] originalProps = getRootProperties();
264
// URL[] originalRoots = getRootURLs();
265
// Map<URL,String> oldRoots2props = new HashMap<URL,String>();
266
// for (int i=0; i<originalProps.length;i++) {
267
// oldRoots2props.put (originalRoots[i],originalProps[i]);
268
// }
269
// Map<URL,String> newRoots2lab = new HashMap<URL,String>();
270
// for (int i=0; i<roots.length;i++) {
271
// newRoots2lab.put (roots[i],labels[i]);
272
// }
273
// Element cfgEl = helper.getPrimaryConfigurationData(true);
274
// NodeList nl = cfgEl.getElementsByTagNameNS(RailsProjectType.PROJECT_CONFIGURATION_NAMESPACE, elementName);
275
// assert nl.getLength() == 1 : "Illegal project.xml"; //NOI18N
276
// Element ownerElement = (Element) nl.item(0);
277
// //Remove all old roots
278
// NodeList rootsNodes = ownerElement.getElementsByTagNameNS(RailsProjectType.PROJECT_CONFIGURATION_NAMESPACE, "root"); //NOI18N
279
// while (rootsNodes.getLength()>0) {
280
// Element root = (Element) rootsNodes.item(0);
281
// ownerElement.removeChild(root);
282
// }
283
// //Remove all unused root properties
284
// List<URL> newRoots = Arrays.asList(roots);
285
// Map<URL,String> propsToRemove = new HashMap<URL,String>(oldRoots2props);
286
// propsToRemove.keySet().removeAll(newRoots);
287
// EditableProperties props = helper.getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
288
// props.keySet().removeAll(propsToRemove.values());
289
// helper.putProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH,props);
290
// //Add the new roots
291
// Document doc = ownerElement.getOwnerDocument();
292
// oldRoots2props.keySet().retainAll(newRoots);
293
// for (URL newRoot : newRoots) {
294
// String rootName = oldRoots2props.get(newRoot);
295
// if (rootName == null) {
296
// //Root is new generate property for it
297
// props = helper.getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
298
// String[] names = newRoot.getPath().split("/"); //NOI18N
299
// rootName = MessageFormat.format(newRootNameTemplate, new Object[] {names[names.length - 1], ""}); // NOI18N
300
// int rootIndex = 1;
301
// while (props.containsKey(rootName)) {
302
// rootIndex++;
303
// rootName = MessageFormat.format(newRootNameTemplate, new Object[] {names[names.length - 1], rootIndex});
304
// }
305
// File f = FileUtil.normalizeFile(new File(URI.create(newRoot.toExternalForm())));
306
// File projDir = FileUtil.toFile(helper.getRakeProjectHelper().getProjectDirectory());
307
// String path = f.getAbsolutePath();
308
// String prjPath = projDir.getAbsolutePath()+File.separatorChar;
309
// if (path.startsWith(prjPath)) {
310
// path = path.substring(prjPath.length());
311
// }
312
// else {
313
// path = refHelper.createForeignFileReference(f, RailsProject.SOURCES_TYPE_RUBY);
314
// props = helper.getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
315
// }
316
// props.put(rootName,path);
317
// helper.putProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH,props);
318
// }
319
// Element newRootNode = doc.createElementNS(RailsProjectType.PROJECT_CONFIGURATION_NAMESPACE, "root"); //NOI18N
320
// newRootNode.setAttribute("id",rootName); //NOI18N
321
// String label = (String) newRoots2lab.get (newRoot);
322
// if (label != null && label.length()>0 && !label.equals (getRootDisplayName(null,rootName))) { //NOI18N
323
// newRootNode.setAttribute("name",label); //NOI18N
324
// }
325
// ownerElement.appendChild (newRootNode);
326
// }
327
// helper.putPrimaryConfigurationData(cfgEl,true);
328
// return null;
329
// }
330
// }
331
// );
332
// }
333

334     /**
335      * Translates root name into display name of source/test root
336      * @param rootName the name of root got from {@link SourceRoots#getRootNames}
337      * @param propName the name of property the root is stored in
338      * @return the label to be displayed
339      */

340     public String JavaDoc getRootDisplayName (String JavaDoc rootName, String JavaDoc propName) {
341         if (rootName == null || rootName.length() ==0) {
342             //If the prop is src.dir use the default name
343
if (isTest && RailsProjectGenerator.DEFAULT_TEST_SRC_NAME.equals(propName)) { //NOI18N
344
rootName = DEFAULT_TEST_LABEL;
345             }
346             else if (!isTest && RailsProjectGenerator.DEFAULT_SRC_NAME.equals(propName)) { //NOI18N
347
rootName = DEFAULT_SOURCE_LABEL;
348             }
349             else {
350                 //If the name is not given, it should be either a relative path in the project dir
351
//or absolute path when the root is not under the project dir
352
String JavaDoc propValue = evaluator.getProperty(propName);
353                 File JavaDoc sourceRoot = propValue == null ? null : helper.getRakeProjectHelper().resolveFile(propValue);
354                 rootName = createInitialDisplayName(sourceRoot);
355             }
356         }
357         return rootName;
358     }
359     
360     /**
361      * Creates initial display name of source/test root
362      * @param sourceRoot the source root
363      * @return the label to be displayed
364      */

365     public String JavaDoc createInitialDisplayName (File JavaDoc sourceRoot) {
366         String JavaDoc rootName;
367         if (sourceRoot != null) {
368         String JavaDoc srPath = sourceRoot.getAbsolutePath();
369         String JavaDoc pdPath = projectDir.getAbsolutePath() + File.separatorChar;
370         if (srPath.startsWith(pdPath)) {
371             rootName = srPath.substring(pdPath.length());
372         }
373         else {
374             rootName = sourceRoot.getAbsolutePath();
375         }
376         }
377         else {
378             rootName = isTest ? DEFAULT_TEST_LABEL : DEFAULT_SOURCE_LABEL;
379         }
380         return rootName;
381     }
382     
383     /**
384      * Returns true if this SourceRoots instance represents source roots belonging to
385      * the tests compilation unit.
386      * @return boolean
387      */

388     public boolean isTest () {
389         return this.isTest;
390     }
391
392     private void resetCache (boolean isXMLChange, String JavaDoc propName) {
393         boolean fire = false;
394         synchronized (this) {
395             //In case of change reset local cache
396
if (isXMLChange) {
397                 this.sourceRootProperties = null;
398                 this.sourceRootNames = null;
399                 this.sourceRoots = null;
400                 this.sourceRootURLs = null;
401                 fire = true;
402             } else if (propName == null || (sourceRootProperties != null && sourceRootProperties.contains(propName))) {
403                 this.sourceRoots = null;
404                 this.sourceRootURLs = null;
405                 fire = true;
406             }
407         }
408         if (fire) {
409             if (isXMLChange) {
410                 this.support.firePropertyChange (PROP_ROOT_PROPERTIES,null,null);
411             }
412             this.support.firePropertyChange (PROP_ROOTS,null,null);
413         }
414     }
415
416     private void readProjectMetadata () {
417         Element JavaDoc cfgEl = helper.getPrimaryConfigurationData(true);
418         NodeList JavaDoc nl = cfgEl.getElementsByTagNameNS(RailsProjectType.PROJECT_CONFIGURATION_NAMESPACE, elementName);
419         assert nl.getLength() == 0 || nl.getLength() == 1 : "Illegal project.xml"; //NOI18N
420
List JavaDoc<String JavaDoc> rootProps = new ArrayList JavaDoc<String JavaDoc>();
421         List JavaDoc<String JavaDoc> rootNames = new ArrayList JavaDoc<String JavaDoc>();
422         // It can be 0 in the case when the project is created by RailsProjectGenerator and not yet customized
423
if (nl.getLength()==1) {
424             NodeList JavaDoc roots = ((Element JavaDoc)nl.item(0)).getElementsByTagNameNS(RailsProjectType.PROJECT_CONFIGURATION_NAMESPACE, "root"); //NOI18N
425
for (int i=0; i<roots.getLength(); i++) {
426                 Element JavaDoc root = (Element JavaDoc) roots.item(i);
427                 String JavaDoc value = root.getAttribute("id"); //NOI18N
428
assert value.length() > 0 : "Illegal project.xml";
429                 rootProps.add(value);
430                 value = root.getAttribute("name"); //NOI18N
431
rootNames.add (value);
432             }
433         }
434         this.sourceRootProperties = Collections.unmodifiableList(rootProps);
435         this.sourceRootNames = Collections.unmodifiableList(rootNames);
436     }
437
438     private class ProjectMetadataListener implements PropertyChangeListener JavaDoc,RakeProjectListener {
439
440         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
441             resetCache (false,evt.getPropertyName());
442         }
443
444         public void configurationXmlChanged(RakeProjectEvent ev) {
445             resetCache (true,null);
446         }
447
448         public void propertiesChanged(RakeProjectEvent ev) {
449             //Handled by propertyChange
450
}
451     }
452
453 }
454
Popular Tags