KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > ruby > spi > project > support > rake > ProjectProperties


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.spi.project.support.rake;
21
22 import java.io.ByteArrayOutputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.util.ArrayList JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Map JavaDoc;
33 import java.util.Properties JavaDoc;
34 import java.util.logging.Logger JavaDoc;
35 import javax.swing.event.ChangeEvent JavaDoc;
36 import javax.swing.event.ChangeListener JavaDoc;
37 import org.netbeans.api.project.ProjectManager;
38 import org.netbeans.modules.ruby.modules.project.rake.FileChangeSupport;
39 import org.netbeans.modules.ruby.modules.project.rake.FileChangeSupportEvent;
40 import org.netbeans.modules.ruby.modules.project.rake.FileChangeSupportListener;
41 import org.netbeans.modules.ruby.modules.project.rake.UserQuestionHandler;
42 import org.openide.ErrorManager;
43 import org.openide.filesystems.FileLock;
44 import org.openide.filesystems.FileObject;
45 import org.openide.filesystems.FileSystem;
46 import org.openide.filesystems.FileUtil;
47 import org.openide.modules.InstalledFileLocator;
48 import org.openide.util.Mutex;
49 import org.openide.util.RequestProcessor;
50 import org.openide.util.UserQuestionException;
51 import org.openide.util.Utilities;
52
53 /**
54  * Manages the loaded property files for {@link RakeProjectHelper}.
55  * @author Jesse Glick
56  */

57 final class ProjectProperties {
58     
59     /** Associated helper. */
60     private final RakeProjectHelper helper;
61     
62     /**
63      * Properties loaded from metadata files on disk.
64      * Keys are project-relative paths such as {@link #PROJECT_PROPERTIES_PATH}.
65      * Values are loaded property providers.
66      */

67     private final Map JavaDoc<String JavaDoc,PP> properties = new HashMap JavaDoc<String JavaDoc,PP>();
68     
69     /** @see #getStockPropertyPreprovider */
70     private PropertyProvider stockPropertyPreprovider = null;
71     
72     /** @see #getStandardPropertyEvaluator */
73     private PropertyEvaluator standardPropertyEvaluator = null;
74     
75     /**
76      * Create a project properties helper object.
77      * @param helper the associated helper
78      */

79     public ProjectProperties(RakeProjectHelper helper) {
80         this.helper = helper;
81     }
82     
83     /**
84      * Get properties from a given path.
85      * @param path the project-relative path
86      * @return the applicable properties (created if empty; never null)
87      */

88     public EditableProperties getProperties(String JavaDoc path) {
89         EditableProperties ep = getPP(path).getEditablePropertiesOrNull();
90         if (ep != null) {
91             return ep.cloneProperties();
92         } else {
93             return new EditableProperties(true);
94         }
95     }
96     
97     /**
98      * Store properties in memory.
99      * @param path the project-relative path
100      * @param props the new properties, or null to remove the properties file
101      * @return true if an actual change was made
102      */

103     public boolean putProperties(String JavaDoc path, EditableProperties props) {
104         return getPP(path).put(props);
105     }
106     
107     /**
108      * Write cached properties to disk.
109      * @param the project-relative path
110      * @throws IOException if the file could not be written
111      */

112     public FileLock write(String JavaDoc path) throws IOException JavaDoc {
113         assert properties.containsKey(path);
114         return getPP(path).write();
115     }
116     
117     /**
118      * Make a property provider that loads from this file
119      * and fires changes when it is written to (even in memory).
120      */

121     public PropertyProvider getPropertyProvider(String JavaDoc path) {
122         return getPP(path);
123     }
124     
125     private PP getPP(String JavaDoc path) {
126         PP pp = properties.get(path);
127         if (pp == null) {
128             pp = new PP(path, helper);
129             properties.put(path, pp);
130         }
131         return pp;
132     }
133     
134     private static final class PP implements PropertyProvider, FileChangeSupportListener {
135         
136         private static final RequestProcessor RP = new RequestProcessor("ProjectProperties.PP.RP"); // NOI18N
137

138         // XXX lock any loaded property files while the project is modified, to prevent manual editing,
139
// and reload any modified files if the project is unmodified
140

141         private final String JavaDoc path;
142         private final RakeProjectHelper helper;
143         private EditableProperties properties = null;
144         private boolean loaded = false;
145         private final List JavaDoc<ChangeListener JavaDoc> listeners = new ArrayList JavaDoc<ChangeListener JavaDoc>();
146         private boolean writing = false;
147         
148         public PP(String JavaDoc path, RakeProjectHelper helper) {
149             this.path = path;
150             this.helper = helper;
151             FileChangeSupport.DEFAULT.addListener(this, new File JavaDoc(FileUtil.toFile(dir()), path.replace('/', File.separatorChar)));
152         }
153         
154         private FileObject dir() {
155             return helper.getProjectDirectory();
156         }
157         
158         public EditableProperties getEditablePropertiesOrNull() {
159             if (!loaded) {
160                 properties = null;
161                 FileObject fo = dir().getFileObject(path);
162                 if (fo != null) {
163                     try {
164                         EditableProperties p;
165                         InputStream JavaDoc is = fo.getInputStream();
166                         try {
167                             p = new EditableProperties(true);
168                             p.load(is);
169                         } finally {
170                             is.close();
171                         }
172                         properties = p;
173                     } catch (IOException JavaDoc e) {
174                         ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
175                     }
176                 }
177                 loaded = true;
178             }
179             return properties;
180         }
181         
182         public boolean put(EditableProperties nue) {
183             loaded = true;
184             boolean modifying = !Utilities.compareObjects(nue, properties);
185             if (modifying) {
186                 if (nue != null) {
187                     properties = nue.cloneProperties();
188                 } else {
189                     properties = null;
190                 }
191                 fireChange();
192             }
193             return modifying;
194         }
195         
196         public FileLock write() throws IOException JavaDoc {
197             assert loaded;
198             final FileObject f = dir().getFileObject(path);
199             assert !writing;
200             final FileLock[] _lock = new FileLock[1];
201             writing = true;
202             try {
203                 if (properties != null) {
204                     // Supposed to create/modify the file.
205
// Need to use an atomic action - otherwise listeners will first
206
// receive an event that the file has been written to zero length
207
// (which for *.properties means no keys), which is wrong.
208
dir().getFileSystem().runAtomicAction(new FileSystem.AtomicAction() {
209                         public void run() throws IOException JavaDoc {
210                             final FileObject _f;
211                             if (f == null) {
212                                 _f = FileUtil.createData(dir(), path);
213                                 assert _f != null : "FU.cD must not return null; called on " + dir() + " + " + path; // #50802
214
} else {
215                                 _f = f;
216                             }
217                             ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
218                             properties.store(baos);
219                             final byte[] data = baos.toByteArray();
220                             try {
221                                 _lock[0] = _f.lock(); // released by {@link RakeProjectHelper#save}
222
OutputStream JavaDoc os = _f.getOutputStream(_lock[0]);
223                                 try {
224                                     os.write(data);
225                                 } finally {
226                                     os.close();
227                                 }
228                             } catch (UserQuestionException uqe) { // #46089
229
helper.needPendingHook();
230                                 UserQuestionHandler.handle(uqe, new UserQuestionHandler.Callback() {
231                                     public void accepted() {
232                                         // Try again.
233
assert !writing;
234                                         writing = true;
235                                         try {
236                                             FileLock lock = _f.lock();
237                                             try {
238                                                 OutputStream JavaDoc os = _f.getOutputStream(lock);
239                                                 try {
240                                                     os.write(data);
241                                                 } finally {
242                                                     os.close();
243                                                 }
244                                             } finally {
245                                                 lock.releaseLock();
246                                             }
247                                             helper.maybeCallPendingHook();
248                                         } catch (IOException JavaDoc e) {
249                                             // Oh well.
250
ErrorManager.getDefault().notify(e);
251                                             reload();
252                                         } finally {
253                                             writing = false;
254                                         }
255                                     }
256                                     public void denied() {
257                                         reload();
258                                     }
259                                     public void error(IOException JavaDoc e) {
260                                         ErrorManager.getDefault().notify(e);
261                                         reload();
262                                     }
263                                     private void reload() {
264                                         helper.cancelPendingHook();
265                                         // Revert the save.
266
diskChange();
267                                     }
268                                 });
269                             }
270                         }
271                     });
272                 } else {
273                     // We are supposed to remove any existing file.
274
if (f != null) {
275                         f.delete();
276                     }
277                 }
278             } catch (IOException JavaDoc e) {
279                 if (_lock[0] != null) {
280                     // Release it now, since no one else will.
281
_lock[0].releaseLock();
282                 }
283                 throw e;
284             } finally {
285                 writing = false;
286             }
287             return _lock[0];
288         }
289         
290         public Map JavaDoc<String JavaDoc,String JavaDoc> getProperties() {
291             Map JavaDoc<String JavaDoc,String JavaDoc> props = getEditablePropertiesOrNull();
292             if (props != null) {
293                 return Collections.unmodifiableMap(props);
294             } else {
295                 return Collections.emptyMap();
296             }
297         }
298         
299         public synchronized void addChangeListener(ChangeListener JavaDoc l) {
300             listeners.add(l);
301         }
302         
303         public synchronized void removeChangeListener(ChangeListener JavaDoc l) {
304             listeners.remove(l);
305         }
306         
307         private void fireChange() {
308             final ChangeListener JavaDoc[] ls;
309             synchronized (this) {
310                 if (listeners.isEmpty()) {
311                     return;
312                 }
313                 ls = listeners.toArray(new ChangeListener JavaDoc[listeners.size()]);
314             }
315             final ChangeEvent JavaDoc ev = new ChangeEvent JavaDoc(this);
316             final Mutex.Action<Void JavaDoc> action = new Mutex.Action<Void JavaDoc>() {
317                 public Void JavaDoc run() {
318                     for (ChangeListener JavaDoc l : ls) {
319                         l.stateChanged(ev);
320                     }
321                     return null;
322                 }
323             };
324             if (ProjectManager.mutex().isWriteAccess()) {
325                 // Run it right now. postReadRequest would be too late.
326
ProjectManager.mutex().readAccess(action);
327             } else if (ProjectManager.mutex().isReadAccess()) {
328                 // Run immediately also. No need to switch to read access.
329
action.run();
330             } else {
331                 // Not safe to acquire a new lock, so run later in read access.
332
RP.post(new Runnable JavaDoc() {
333                     public void run() {
334                         ProjectManager.mutex().readAccess(action);
335                     }
336                 });
337             }
338         }
339         
340         private void diskChange() {
341             // XXX should check for a possible clobber from in-memory data
342
if (!writing) {
343                 loaded = false;
344             }
345             fireChange();
346             if (!writing) {
347                 helper.fireExternalChange(path);
348             }
349         }
350
351         public void fileCreated(FileChangeSupportEvent event) {
352             diskChange();
353         }
354
355         public void fileDeleted(FileChangeSupportEvent event) {
356             diskChange();
357         }
358
359         public void fileModified(FileChangeSupportEvent event) {
360             diskChange();
361         }
362         
363     }
364
365     /**
366      * See {@link RakeProjectHelper#getStockPropertyPreprovider}.
367      */

368     public PropertyProvider getStockPropertyPreprovider() {
369         if (stockPropertyPreprovider == null) {
370             Map JavaDoc<String JavaDoc,String JavaDoc> m = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
371             Properties JavaDoc p = System.getProperties();
372             synchronized (p) {
373                 for (Map.Entry JavaDoc<Object JavaDoc,Object JavaDoc> entry : p.entrySet()) {
374                     try {
375                         m.put((String JavaDoc) entry.getKey(), (String JavaDoc) entry.getValue());
376                     } catch (ClassCastException JavaDoc e) {
377                         Logger.getLogger(ProjectProperties.class.getName()).warning(
378                                 "WARNING: removing non-String-valued system property " + entry.getKey() + "=" + entry.getValue() + " (cf. #45788)");
379                     }
380                 }
381             }
382             m.put("basedir", FileUtil.toFile(helper.getProjectDirectory()).getAbsolutePath()); // NOI18N
383
File JavaDoc antJar = InstalledFileLocator.getDefault().locate("ant/lib/ant.jar", "org.apache.tools.ant.module", false); // NOI18N
384
if (antJar != null) {
385                 File JavaDoc antHome = antJar.getParentFile().getParentFile();
386                 m.put("ant.home", antHome.getAbsolutePath()); // NOI18N
387
}
388             stockPropertyPreprovider = PropertyUtils.fixedPropertyProvider(m);
389         }
390         return stockPropertyPreprovider;
391     }
392     
393     /**
394      * See {@link RakeProjectHelper#getStandardPropertyEvaluator}.
395      */

396     public PropertyEvaluator getStandardPropertyEvaluator() {
397         if (standardPropertyEvaluator == null) {
398             PropertyEvaluator findUserPropertiesFile = PropertyUtils.sequentialPropertyEvaluator(
399                 getStockPropertyPreprovider(),
400                 getPropertyProvider(RakeProjectHelper.PRIVATE_PROPERTIES_PATH));
401             PropertyProvider globalProperties = PropertyUtils.userPropertiesProvider(findUserPropertiesFile,
402                     "user.properties.file", FileUtil.toFile(helper.getProjectDirectory())); // NOI18N
403
standardPropertyEvaluator = PropertyUtils.sequentialPropertyEvaluator(
404                 getStockPropertyPreprovider(),
405                 getPropertyProvider(RakeProjectHelper.PRIVATE_PROPERTIES_PATH),
406                 globalProperties,
407                 getPropertyProvider(RakeProjectHelper.PROJECT_PROPERTIES_PATH));
408         }
409         return standardPropertyEvaluator;
410     }
411     
412 }
413
Popular Tags