KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ruby > rubyproject > 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.rubyproject;
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                 synchronized (SourceRoots.this) {
114                     if (sourceRootNames == null) {
115                         readProjectMetadata();
116                     }
117                 }
118                 return sourceRootNames.toArray (new String JavaDoc[sourceRootNames.size()]);
119             }
120         });
121     }
122
123     /**
124      * Returns names of Ant properties in the project.properties file holding the source roots.
125      * @return an array of String
126      */

127     public String JavaDoc[] getRootProperties () {
128         return ProjectManager.mutex().readAccess(new Mutex.Action<String JavaDoc[]>() {
129             public String JavaDoc[] run() {
130                 synchronized (SourceRoots.this) {
131                     if (sourceRootProperties == null) {
132                         readProjectMetadata();
133                     }
134                 }
135                 return sourceRootProperties.toArray (new String JavaDoc[sourceRootProperties.size()]);
136             }
137         });
138     }
139
140     /**
141      * Returns the source roots
142      * @return an array of FileObject
143      */

144     public FileObject[] getRoots () {
145         return ProjectManager.mutex().readAccess(new Mutex.Action<FileObject[]>() {
146                 public FileObject[] run () {
147                     synchronized (this) {
148                         //Local caching
149
if (sourceRoots == null) {
150                             String JavaDoc[] srcProps = getRootProperties();
151                             List JavaDoc<FileObject> result = new ArrayList JavaDoc<FileObject>();
152                             for (String JavaDoc p : srcProps) {
153                                 String JavaDoc prop = evaluator.getProperty(p);
154                                 if (prop != null) {
155                                     FileObject f = helper.getRakeProjectHelper().resolveFileObject(prop);
156                                     if (f == null) {
157                                         continue;
158                                     }
159                                     if (FileUtil.isArchiveFile(f)) {
160                                         f = FileUtil.getArchiveRoot(f);
161                                     }
162                                     result.add(f);
163                                 }
164                             }
165                             sourceRoots = Collections.unmodifiableList(result);
166                         }
167                     }
168                     return sourceRoots.toArray(new FileObject[sourceRoots.size()]);
169                 }
170         });
171     }
172
173     /**
174      * Returns the source roots as URLs.
175      * @return an array of URL
176      */

177     public URL JavaDoc[] getRootURLs() {
178         return ProjectManager.mutex().readAccess(new Mutex.Action<URL JavaDoc[]>() {
179             public URL JavaDoc[] run () {
180                 synchronized (this) {
181                     //Local caching
182
if (sourceRootURLs == null) {
183                         String JavaDoc[] srcProps = getRootProperties();
184                         List JavaDoc<URL JavaDoc> result = new ArrayList JavaDoc<URL JavaDoc>();
185                         for (int i = 0; i<srcProps.length; i++) {
186                             String JavaDoc prop = evaluator.getProperty(srcProps[i]);
187                             if (prop != null) {
188                                 File JavaDoc f = helper.getRakeProjectHelper().resolveFile(prop);
189                                 try {
190                                     URL JavaDoc url = f.toURI().toURL();
191                                     if (!f.exists()) {
192                                         url = new URL JavaDoc(url.toExternalForm() + "/"); // NOI18N
193
}
194                                     result.add(url);
195                                 } catch (MalformedURLException JavaDoc e) {
196                                     ErrorManager.getDefault().notify(e);
197                                 }
198                             }
199                         }
200                         sourceRootURLs = Collections.unmodifiableList(result);
201                     }
202                 }
203                 return sourceRootURLs.toArray(new URL JavaDoc[sourceRootURLs.size()]);
204             }
205         });
206     }
207
208     /**
209      * Adds PropertyChangeListener
210      * @param listener
211      */

212     public void addPropertyChangeListener (PropertyChangeListener JavaDoc listener) {
213         this.support.addPropertyChangeListener (listener);
214     }
215
216     /**
217      * Removes PropertyChangeListener
218      * @param listener
219      */

220     public void removePropertyChangeListener (PropertyChangeListener JavaDoc listener) {
221         this.support.removePropertyChangeListener (listener);
222     }
223
224
225     /**
226      * Replaces the current roots by the new ones
227      * @param roots the URLs of new roots
228      * @param labels the names of roots
229      */

230     public void putRoots (final URL JavaDoc[] roots, final String JavaDoc[] labels) {
231         ProjectManager.mutex().writeAccess(
232                 new Mutex.Action<Void JavaDoc>() {
233                     public Void JavaDoc run() {
234                         String JavaDoc[] originalProps = getRootProperties();
235                         URL JavaDoc[] originalRoots = getRootURLs();
236                         Map JavaDoc<URL JavaDoc,String JavaDoc> oldRoots2props = new HashMap JavaDoc<URL JavaDoc,String JavaDoc>();
237                         for (int i=0; i<originalProps.length;i++) {
238                             oldRoots2props.put (originalRoots[i],originalProps[i]);
239                         }
240                         Map JavaDoc<URL JavaDoc,String JavaDoc> newRoots2lab = new HashMap JavaDoc<URL JavaDoc,String JavaDoc>();
241                         for (int i=0; i<roots.length;i++) {
242                             newRoots2lab.put (roots[i],labels[i]);
243                         }
244                         Element JavaDoc cfgEl = helper.getPrimaryConfigurationData(true);
245                         NodeList JavaDoc nl = cfgEl.getElementsByTagNameNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, elementName);
246                         assert nl.getLength() == 1 : "Illegal project.xml"; //NOI18N
247
Element JavaDoc ownerElement = (Element JavaDoc) nl.item(0);
248                         //Remove all old roots
249
NodeList JavaDoc rootsNodes = ownerElement.getElementsByTagNameNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, "root"); //NOI18N
250
while (rootsNodes.getLength()>0) {
251                             Element JavaDoc root = (Element JavaDoc) rootsNodes.item(0);
252                             ownerElement.removeChild(root);
253                         }
254                         //Remove all unused root properties
255
List JavaDoc<URL JavaDoc> newRoots = Arrays.asList(roots);
256                         Map JavaDoc<URL JavaDoc,String JavaDoc> propsToRemove = new HashMap JavaDoc<URL JavaDoc,String JavaDoc>(oldRoots2props);
257                         propsToRemove.keySet().removeAll(newRoots);
258                         EditableProperties props = helper.getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
259                         props.keySet().removeAll(propsToRemove.values());
260                         helper.putProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH,props);
261                         //Add the new roots
262
Document JavaDoc doc = ownerElement.getOwnerDocument();
263                         oldRoots2props.keySet().retainAll(newRoots);
264                         for (URL JavaDoc newRoot : newRoots) {
265                             String JavaDoc rootName = oldRoots2props.get(newRoot);
266                             if (rootName == null) {
267                                 //Root is new generate property for it
268
props = helper.getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
269                                 String JavaDoc[] names = newRoot.getPath().split("/"); //NOI18N
270
rootName = MessageFormat.format(newRootNameTemplate, new Object JavaDoc[] {names[names.length - 1], ""}); // NOI18N
271
int rootIndex = 1;
272                                 while (props.containsKey(rootName)) {
273                                     rootIndex++;
274                                     rootName = MessageFormat.format(newRootNameTemplate, new Object JavaDoc[] {names[names.length - 1], rootIndex});
275                                 }
276                                 File JavaDoc f = FileUtil.normalizeFile(new File JavaDoc(URI.create(newRoot.toExternalForm())));
277                                 File JavaDoc projDir = FileUtil.toFile(helper.getRakeProjectHelper().getProjectDirectory());
278                                 String JavaDoc path = f.getAbsolutePath();
279                                 String JavaDoc prjPath = projDir.getAbsolutePath()+File.separatorChar;
280                                 if (path.startsWith(prjPath)) {
281                                     path = path.substring(prjPath.length());
282                                 }
283                                 else {
284                                     path = refHelper.createForeignFileReference(f, RubyProject.SOURCES_TYPE_RUBY);
285                                     props = helper.getProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH);
286                                 }
287                                 props.put(rootName,path);
288                                 helper.putProperties(RakeProjectHelper.PROJECT_PROPERTIES_PATH,props);
289                             }
290                             Element JavaDoc newRootNode = doc.createElementNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, "root"); //NOI18N
291
newRootNode.setAttribute("id",rootName); //NOI18N
292
String JavaDoc label = (String JavaDoc) newRoots2lab.get (newRoot);
293                             if (label != null && label.length()>0 && !label.equals (getRootDisplayName(null,rootName))) { //NOI18N
294
newRootNode.setAttribute("name",label); //NOI18N
295
}
296                             ownerElement.appendChild (newRootNode);
297                         }
298                         helper.putPrimaryConfigurationData(cfgEl,true);
299                         return null;
300                     }
301                 }
302         );
303     }
304     
305     /**
306      * Translates root name into display name of source/test root
307      * @param rootName the name of root got from {@link SourceRoots#getRootNames}
308      * @param propName the name of property the root is stored in
309      * @return the label to be displayed
310      */

311     public String JavaDoc getRootDisplayName (String JavaDoc rootName, String JavaDoc propName) {
312         if (rootName == null || rootName.length() ==0) {
313             //If the prop is src.dir use the default name
314
if (isTest && RubyProjectGenerator.DEFAULT_TEST_SRC_NAME.equals(propName)) { //NOI18N
315
rootName = DEFAULT_TEST_LABEL;
316             }
317             else if (!isTest && RubyProjectGenerator.DEFAULT_SRC_NAME.equals(propName)) { //NOI18N
318
rootName = DEFAULT_SOURCE_LABEL;
319             }
320             else {
321                 //If the name is not given, it should be either a relative path in the project dir
322
//or absolute path when the root is not under the project dir
323
String JavaDoc propValue = evaluator.getProperty(propName);
324                 File JavaDoc sourceRoot = propValue == null ? null : helper.getRakeProjectHelper().resolveFile(propValue);
325                 rootName = createInitialDisplayName(sourceRoot);
326             }
327         }
328         return rootName;
329     }
330     
331     /**
332      * Creates initial display name of source/test root
333      * @param sourceRoot the source root
334      * @return the label to be displayed
335      */

336     public String JavaDoc createInitialDisplayName (File JavaDoc sourceRoot) {
337         String JavaDoc rootName;
338         if (sourceRoot != null) {
339         String JavaDoc srPath = sourceRoot.getAbsolutePath();
340         String JavaDoc pdPath = projectDir.getAbsolutePath() + File.separatorChar;
341         if (srPath.startsWith(pdPath)) {
342             rootName = srPath.substring(pdPath.length());
343         }
344         else {
345             rootName = sourceRoot.getAbsolutePath();
346         }
347         }
348         else {
349             rootName = isTest ? DEFAULT_TEST_LABEL : DEFAULT_SOURCE_LABEL;
350         }
351         return rootName;
352     }
353     
354     /**
355      * Returns true if this SourceRoots instance represents source roots belonging to
356      * the tests compilation unit.
357      * @return boolean
358      */

359     public boolean isTest () {
360         return this.isTest;
361     }
362
363     private void resetCache (boolean isXMLChange, String JavaDoc propName) {
364         boolean fire = false;
365         synchronized (this) {
366             //In case of change reset local cache
367
if (isXMLChange) {
368                 this.sourceRootProperties = null;
369                 this.sourceRootNames = null;
370                 this.sourceRoots = null;
371                 this.sourceRootURLs = null;
372                 fire = true;
373             } else if (propName == null || (sourceRootProperties != null && sourceRootProperties.contains(propName))) {
374                 this.sourceRoots = null;
375                 this.sourceRootURLs = null;
376                 fire = true;
377             }
378         }
379         if (fire) {
380             if (isXMLChange) {
381                 this.support.firePropertyChange (PROP_ROOT_PROPERTIES,null,null);
382             }
383             this.support.firePropertyChange (PROP_ROOTS,null,null);
384         }
385     }
386
387     private void readProjectMetadata () {
388         Element JavaDoc cfgEl = helper.getPrimaryConfigurationData(true);
389         NodeList JavaDoc nl = cfgEl.getElementsByTagNameNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, elementName);
390         assert nl.getLength() == 0 || nl.getLength() == 1 : "Illegal project.xml"; //NOI18N
391
List JavaDoc<String JavaDoc> rootProps = new ArrayList JavaDoc<String JavaDoc>();
392         List JavaDoc<String JavaDoc> rootNames = new ArrayList JavaDoc<String JavaDoc>();
393         // It can be 0 in the case when the project is created by RubyProjectGenerator and not yet customized
394
if (nl.getLength()==1) {
395             NodeList JavaDoc roots = ((Element JavaDoc)nl.item(0)).getElementsByTagNameNS(RubyProjectType.PROJECT_CONFIGURATION_NAMESPACE, "root"); //NOI18N
396
for (int i=0; i<roots.getLength(); i++) {
397                 Element JavaDoc root = (Element JavaDoc) roots.item(i);
398                 String JavaDoc value = root.getAttribute("id"); //NOI18N
399
assert value.length() > 0 : "Illegal project.xml";
400                 rootProps.add(value);
401                 value = root.getAttribute("name"); //NOI18N
402
rootNames.add (value);
403             }
404         }
405         this.sourceRootProperties = Collections.unmodifiableList(rootProps);
406         this.sourceRootNames = Collections.unmodifiableList(rootNames);
407     }
408
409     private class ProjectMetadataListener implements PropertyChangeListener JavaDoc,RakeProjectListener {
410
411         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
412             resetCache (false,evt.getPropertyName());
413         }
414
415         public void configurationXmlChanged(RakeProjectEvent ev) {
416             resetCache (true,null);
417         }
418
419         public void propertiesChanged(RakeProjectEvent ev) {
420             //Handled by propertyChange
421
}
422     }
423
424 }
425
Popular Tags