1 19 20 package org.netbeans.modules.ruby.spi.project.support.rake; 21 22 import java.io.ByteArrayOutputStream ; 23 import java.io.File ; 24 import java.io.IOException ; 25 import java.io.InputStream ; 26 import java.io.OutputStream ; 27 import java.util.ArrayList ; 28 import java.util.Collections ; 29 import java.util.HashMap ; 30 import java.util.Iterator ; 31 import java.util.List ; 32 import java.util.Map ; 33 import java.util.Properties ; 34 import java.util.logging.Logger ; 35 import javax.swing.event.ChangeEvent ; 36 import javax.swing.event.ChangeListener ; 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 57 final class ProjectProperties { 58 59 60 private final RakeProjectHelper helper; 61 62 67 private final Map <String ,PP> properties = new HashMap <String ,PP>(); 68 69 70 private PropertyProvider stockPropertyPreprovider = null; 71 72 73 private PropertyEvaluator standardPropertyEvaluator = null; 74 75 79 public ProjectProperties(RakeProjectHelper helper) { 80 this.helper = helper; 81 } 82 83 88 public EditableProperties getProperties(String 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 103 public boolean putProperties(String path, EditableProperties props) { 104 return getPP(path).put(props); 105 } 106 107 112 public FileLock write(String path) throws IOException { 113 assert properties.containsKey(path); 114 return getPP(path).write(); 115 } 116 117 121 public PropertyProvider getPropertyProvider(String path) { 122 return getPP(path); 123 } 124 125 private PP getPP(String 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"); 138 141 private final String path; 142 private final RakeProjectHelper helper; 143 private EditableProperties properties = null; 144 private boolean loaded = false; 145 private final List <ChangeListener > listeners = new ArrayList <ChangeListener >(); 146 private boolean writing = false; 147 148 public PP(String path, RakeProjectHelper helper) { 149 this.path = path; 150 this.helper = helper; 151 FileChangeSupport.DEFAULT.addListener(this, new File (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 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 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 { 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 dir().getFileSystem().runAtomicAction(new FileSystem.AtomicAction() { 209 public void run() throws IOException { 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; } else { 215 _f = f; 216 } 217 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 218 properties.store(baos); 219 final byte[] data = baos.toByteArray(); 220 try { 221 _lock[0] = _f.lock(); OutputStream os = _f.getOutputStream(_lock[0]); 223 try { 224 os.write(data); 225 } finally { 226 os.close(); 227 } 228 } catch (UserQuestionException uqe) { helper.needPendingHook(); 230 UserQuestionHandler.handle(uqe, new UserQuestionHandler.Callback() { 231 public void accepted() { 232 assert !writing; 234 writing = true; 235 try { 236 FileLock lock = _f.lock(); 237 try { 238 OutputStream 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 e) { 249 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 e) { 260 ErrorManager.getDefault().notify(e); 261 reload(); 262 } 263 private void reload() { 264 helper.cancelPendingHook(); 265 diskChange(); 267 } 268 }); 269 } 270 } 271 }); 272 } else { 273 if (f != null) { 275 f.delete(); 276 } 277 } 278 } catch (IOException e) { 279 if (_lock[0] != null) { 280 _lock[0].releaseLock(); 282 } 283 throw e; 284 } finally { 285 writing = false; 286 } 287 return _lock[0]; 288 } 289 290 public Map <String ,String > getProperties() { 291 Map <String ,String > 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 l) { 300 listeners.add(l); 301 } 302 303 public synchronized void removeChangeListener(ChangeListener l) { 304 listeners.remove(l); 305 } 306 307 private void fireChange() { 308 final ChangeListener [] ls; 309 synchronized (this) { 310 if (listeners.isEmpty()) { 311 return; 312 } 313 ls = listeners.toArray(new ChangeListener [listeners.size()]); 314 } 315 final ChangeEvent ev = new ChangeEvent (this); 316 final Mutex.Action<Void > action = new Mutex.Action<Void >() { 317 public Void run() { 318 for (ChangeListener l : ls) { 319 l.stateChanged(ev); 320 } 321 return null; 322 } 323 }; 324 if (ProjectManager.mutex().isWriteAccess()) { 325 ProjectManager.mutex().readAccess(action); 327 } else if (ProjectManager.mutex().isReadAccess()) { 328 action.run(); 330 } else { 331 RP.post(new Runnable () { 333 public void run() { 334 ProjectManager.mutex().readAccess(action); 335 } 336 }); 337 } 338 } 339 340 private void diskChange() { 341 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 368 public PropertyProvider getStockPropertyPreprovider() { 369 if (stockPropertyPreprovider == null) { 370 Map <String ,String > m = new HashMap <String ,String >(); 371 Properties p = System.getProperties(); 372 synchronized (p) { 373 for (Map.Entry <Object ,Object > entry : p.entrySet()) { 374 try { 375 m.put((String ) entry.getKey(), (String ) entry.getValue()); 376 } catch (ClassCastException 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()); File antJar = InstalledFileLocator.getDefault().locate("ant/lib/ant.jar", "org.apache.tools.ant.module", false); if (antJar != null) { 385 File antHome = antJar.getParentFile().getParentFile(); 386 m.put("ant.home", antHome.getAbsolutePath()); } 388 stockPropertyPreprovider = PropertyUtils.fixedPropertyProvider(m); 389 } 390 return stockPropertyPreprovider; 391 } 392 393 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())); 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 |