KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > spi > project > support > ant > 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.spi.project.support.ant;
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.List JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Properties JavaDoc;
33 import javax.swing.event.ChangeEvent JavaDoc;
34 import javax.swing.event.ChangeListener JavaDoc;
35 import org.netbeans.api.project.ProjectManager;
36 import org.netbeans.modules.project.ant.FileChangeSupport;
37 import org.netbeans.modules.project.ant.FileChangeSupportEvent;
38 import org.netbeans.modules.project.ant.FileChangeSupportListener;
39 import org.netbeans.modules.project.ant.UserQuestionHandler;
40 import org.openide.ErrorManager;
41 import org.openide.filesystems.FileLock;
42 import org.openide.filesystems.FileObject;
43 import org.openide.filesystems.FileSystem;
44 import org.openide.filesystems.FileUtil;
45 import org.openide.modules.InstalledFileLocator;
46 import org.openide.util.Mutex;
47 import org.openide.util.NbCollections;
48 import org.openide.util.RequestProcessor;
49 import org.openide.util.UserQuestionException;
50 import org.openide.util.Utilities;
51
52 /**
53  * Manages the loaded property files for {@link AntProjectHelper}.
54  * @author Jesse Glick
55  */

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

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

78     public ProjectProperties(AntProjectHelper helper) {
79         this.helper = helper;
80     }
81
82     /**
83      * Reclaim some memory by discarding cache.
84      * @see "issue #90195"
85      */

86     public void clear() {
87         properties.clear();
88     }
89     
90     /**
91      * Get properties from a given path.
92      * @param path the project-relative path
93      * @return the applicable properties (created if empty; never null)
94      */

95     public EditableProperties getProperties(String JavaDoc path) {
96         EditableProperties ep = getPP(path).getEditablePropertiesOrNull();
97         if (ep != null) {
98             return ep.cloneProperties();
99         } else {
100             return new EditableProperties(true);
101         }
102     }
103     
104     /**
105      * Store properties in memory.
106      * @param path the project-relative path
107      * @param props the new properties, or null to remove the properties file
108      * @return true if an actual change was made
109      */

110     public boolean putProperties(String JavaDoc path, EditableProperties props) {
111         return getPP(path).put(props);
112     }
113     
114     /**
115      * Write cached properties to disk.
116      * @param the project-relative path
117      * @throws IOException if the file could not be written
118      */

119     public FileLock write(String JavaDoc path) throws IOException JavaDoc {
120         assert properties.containsKey(path);
121         return getPP(path).write();
122     }
123     
124     /**
125      * Make a property provider that loads from this file
126      * and fires changes when it is written to (even in memory).
127      */

128     public PropertyProvider getPropertyProvider(String JavaDoc path) {
129         return getPP(path);
130     }
131     
132     private PP getPP(String JavaDoc path) {
133         PP pp = properties.get(path);
134         if (pp == null) {
135             pp = new PP(path, helper);
136             properties.put(path, pp);
137         }
138         return pp;
139     }
140     
141     private static final class PP implements PropertyProvider, FileChangeSupportListener {
142         
143         private static final RequestProcessor RP = new RequestProcessor("ProjectProperties.PP.RP"); // NOI18N
144

145         // XXX lock any loaded property files while the project is modified, to prevent manual editing,
146
// and reload any modified files if the project is unmodified
147

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

375     public PropertyProvider getStockPropertyPreprovider() {
376         if (stockPropertyPreprovider == null) {
377             Map JavaDoc<String JavaDoc,String JavaDoc> m;
378             Properties JavaDoc p = System.getProperties();
379             synchronized (p) {
380                 m = NbCollections.checkedMapByCopy(p, String JavaDoc.class, String JavaDoc.class, false);
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 AntProjectHelper#getStandardPropertyEvaluator}.
395      */

396     public PropertyEvaluator getStandardPropertyEvaluator() {
397         if (standardPropertyEvaluator == null) {
398             PropertyEvaluator findUserPropertiesFile = PropertyUtils.sequentialPropertyEvaluator(
399                 getStockPropertyPreprovider(),
400                 getPropertyProvider(AntProjectHelper.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(AntProjectHelper.PRIVATE_PROPERTIES_PATH),
406                 globalProperties,
407                 getPropertyProvider(AntProjectHelper.PROJECT_PROPERTIES_PATH));
408         }
409         return standardPropertyEvaluator;
410     }
411     
412 }
413
Popular Tags