KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > j2seproject > 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.java.j2seproject;
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.spi.project.support.ant.AntProjectHelper;
47 import org.netbeans.spi.project.support.ant.AntProjectEvent;
48 import org.netbeans.spi.project.support.ant.AntProjectListener;
49 import org.netbeans.spi.project.support.ant.EditableProperties;
50 import org.netbeans.spi.project.support.ant.PropertyEvaluator;
51 import org.netbeans.spi.project.support.ant.ReferenceHelper;
52 import org.netbeans.api.project.ProjectManager;
53 import org.netbeans.api.java.project.JavaProjectConstants;
54
55 /**
56  * This class represents a project source roots. It is used to obtain roots as Ant properties, FileObject's
57  * or URLs.
58  * @author Tomas Zezula
59  */

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

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

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

111     public String JavaDoc[] getRootNames () {
112         return ProjectManager.mutex().readAccess(new Mutex.Action<String JavaDoc[]>() {
113             public String JavaDoc[] run() {
114                 synchronized (SourceRoots.this) {
115                     if (sourceRootNames == null) {
116                         readProjectMetadata();
117                     }
118                 }
119                 return sourceRootNames.toArray (new String JavaDoc[sourceRootNames.size()]);
120             }
121         });
122     }
123
124     /**
125      * Returns names of Ant properties in the project.properties file holding the source roots.
126      * @return an array of String
127      */

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

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

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

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

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

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

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

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

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