1 /******************************************************************************* 2 * Copyright (c) 2004, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Markus Schorn (Wind River) - [108066] Project prefs marked dirty on read 14 * James Blackburn (Broadcom Corp.) - ongoing development 15 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 473427, 483529 16 *******************************************************************************/ 17 package org.eclipse.core.internal.resources; 18 19 import java.io.*; 20 import java.text.MessageFormat; 21 import java.util.*; 22 import org.eclipse.core.internal.preferences.*; 23 import org.eclipse.core.internal.utils.*; 24 import org.eclipse.core.resources.*; 25 import org.eclipse.core.runtime.*; 26 import org.eclipse.core.runtime.jobs.ISchedulingRule; 27 import org.eclipse.core.runtime.jobs.MultiRule; 28 import org.eclipse.core.runtime.preferences.IEclipsePreferences; 29 import org.eclipse.core.runtime.preferences.IExportedPreferences; 30 import org.eclipse.osgi.util.NLS; 31 import org.osgi.service.prefs.BackingStoreException; 32 import org.osgi.service.prefs.Preferences; 33 34 /** 35 * Represents a node in the Eclipse preference hierarchy which stores preference 36 * values for projects. 37 * 38 * @since 3.0 39 */ 40 public class ProjectPreferences extends EclipsePreferences { 41 static final String PREFS_REGULAR_QUALIFIER = ResourcesPlugin.PI_RESOURCES; 42 static final String PREFS_DERIVED_QUALIFIER = PREFS_REGULAR_QUALIFIER + ".derived"; //$NON-NLS-1$ 43 /** 44 * Cache which nodes have been loaded from disk 45 */ 46 protected static Set<String> loadedNodes = Collections.synchronizedSet(new HashSet<String>()); 47 private IFile file; 48 private boolean initialized = false; 49 /** 50 * Flag indicating that this node is currently reading values from disk, 51 * to avoid flushing during a read. 52 */ 53 private boolean isReading; 54 /** 55 * Flag indicating that this node is currently writing values to disk, 56 * to avoid re-reading after the write completes. 57 */ 58 private boolean isWriting; 59 private IEclipsePreferences loadLevel; 60 private IProject project; 61 private String qualifier; 62 63 // cache 64 private int segmentCount; 65 deleted(IFile file)66 static void deleted(IFile file) throws CoreException { 67 IPath path = file.getFullPath(); 68 int count = path.segmentCount(); 69 if (count != 3) 70 return; 71 // check if we are in the .settings directory 72 if (!EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1))) 73 return; 74 Preferences root = Platform.getPreferencesService().getRootNode(); 75 String project = path.segment(0); 76 String qualifier = path.removeFileExtension().lastSegment(); 77 ProjectPreferences projectNode = (ProjectPreferences) root.node(ProjectScope.SCOPE).node(project); 78 // if the node isn't known then just return 79 try { 80 if (!projectNode.nodeExists(qualifier)) 81 return; 82 } catch (BackingStoreException e) { 83 // ignore 84 } 85 86 // clear the preferences 87 clearNode(projectNode.node(qualifier)); 88 89 // notifies the CharsetManager if needed 90 if (qualifier.equals(PREFS_REGULAR_QUALIFIER) || qualifier.equals(PREFS_DERIVED_QUALIFIER)) 91 preferencesChanged(file.getProject()); 92 } 93 deleted(IFolder folder)94 static void deleted(IFolder folder) throws CoreException { 95 IPath path = folder.getFullPath(); 96 int count = path.segmentCount(); 97 if (count != 2) 98 return; 99 // check if we are the .settings directory 100 if (!EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1))) 101 return; 102 Preferences root = Platform.getPreferencesService().getRootNode(); 103 // The settings dir has been removed/moved so remove all project prefs 104 // for the resource. 105 String project = path.segment(0); 106 Preferences projectNode = root.node(ProjectScope.SCOPE).node(project); 107 // check if we need to notify the charset manager 108 boolean hasResourcesSettings = getFile(folder, PREFS_REGULAR_QUALIFIER).exists() || getFile(folder, PREFS_DERIVED_QUALIFIER).exists(); 109 // remove the preferences 110 removeNode(projectNode); 111 // notifies the CharsetManager 112 if (hasResourcesSettings) 113 preferencesChanged(folder.getProject()); 114 } 115 116 /* 117 * The whole project has been removed so delete all of the project settings 118 */ deleted(IProject project)119 static void deleted(IProject project) throws CoreException { 120 // The settings dir has been removed/moved so remove all project prefs 121 // for the resource. We have to do this now because (since we aren't 122 // synchronizing) there is short-circuit code that doesn't visit the 123 // children. 124 Preferences root = Platform.getPreferencesService().getRootNode(); 125 Preferences projectNode = root.node(ProjectScope.SCOPE).node(project.getName()); 126 // check if we need to notify the charset manager 127 boolean hasResourcesSettings = getFile(project, PREFS_REGULAR_QUALIFIER).exists() || getFile(project, PREFS_DERIVED_QUALIFIER).exists(); 128 // remove the preferences 129 removeNode(projectNode); 130 // notifies the CharsetManager 131 if (hasResourcesSettings) 132 preferencesChanged(project); 133 } 134 deleted(IResource resource)135 static void deleted(IResource resource) throws CoreException { 136 switch (resource.getType()) { 137 case IResource.FILE : 138 deleted((IFile) resource); 139 return; 140 case IResource.FOLDER : 141 deleted((IFolder) resource); 142 return; 143 case IResource.PROJECT : 144 deleted((IProject) resource); 145 return; 146 } 147 } 148 149 /* 150 * Return the preferences file for the given folder and qualifier. 151 */ getFile(IFolder folder, String qualifier)152 static IFile getFile(IFolder folder, String qualifier) { 153 Assert.isLegal(folder.getName().equals(DEFAULT_PREFERENCES_DIRNAME)); 154 return folder.getFile(new Path(qualifier).addFileExtension(PREFS_FILE_EXTENSION)); 155 } 156 157 /* 158 * Return the preferences file for the given project and qualifier. 159 */ getFile(IProject project, String qualifier)160 static IFile getFile(IProject project, String qualifier) { 161 return project.getFile(new Path(DEFAULT_PREFERENCES_DIRNAME).append(qualifier).addFileExtension(PREFS_FILE_EXTENSION)); 162 } 163 loadProperties(IFile file)164 private static Properties loadProperties(IFile file) throws BackingStoreException { 165 if (Policy.DEBUG_PREFERENCES) 166 Policy.debug("Loading preferences from file: " + file.getFullPath()); //$NON-NLS-1$ 167 Properties result = new Properties(); 168 try ( 169 InputStream input = new BufferedInputStream(file.getContents(true)); 170 ) { 171 result.load(input); 172 } catch (CoreException e) { 173 if (e.getStatus().getCode() == IResourceStatus.RESOURCE_NOT_FOUND) { 174 if (Policy.DEBUG_PREFERENCES) 175 Policy.debug(MessageFormat.format("Preference file {0} does not exist.", file.getFullPath())); //$NON-NLS-1$ 176 } else { 177 String message = NLS.bind(Messages.preferences_loadException, file.getFullPath()); 178 log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e)); 179 throw new BackingStoreException(message); 180 } 181 } catch (IOException e) { 182 String message = NLS.bind(Messages.preferences_loadException, file.getFullPath()); 183 log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e)); 184 throw new BackingStoreException(message); 185 } 186 return result; 187 } 188 preferencesChanged(IProject project)189 private static void preferencesChanged(IProject project) { 190 Workspace workspace = ((Workspace) ResourcesPlugin.getWorkspace()); 191 workspace.getCharsetManager().projectPreferencesChanged(project); 192 workspace.getContentDescriptionManager().projectPreferencesChanged(project); 193 } 194 read(ProjectPreferences node, IFile file)195 private static void read(ProjectPreferences node, IFile file) throws BackingStoreException, CoreException { 196 if (file == null || !file.exists()) { 197 if (Policy.DEBUG_PREFERENCES) 198 Policy.debug("Unable to determine preference file or file does not exist for node: " + node.absolutePath()); //$NON-NLS-1$ 199 return; 200 } 201 Properties fromDisk = loadProperties(file); 202 // no work to do 203 if (fromDisk.isEmpty()) 204 return; 205 // create a new node to store the preferences in. 206 IExportedPreferences myNode = (IExportedPreferences) ExportedPreferences.newRoot().node(node.absolutePath()); 207 convertFromProperties((EclipsePreferences) myNode, fromDisk, false); 208 //flag that we are currently reading, to avoid unnecessary writing 209 boolean oldIsReading = node.isReading; 210 node.isReading = true; 211 try { 212 Platform.getPreferencesService().applyPreferences(myNode); 213 } finally { 214 node.isReading = oldIsReading; 215 } 216 } 217 removeNode(Preferences node)218 static void removeNode(Preferences node) throws CoreException { 219 String message = NLS.bind(Messages.preferences_removeNodeException, node.absolutePath()); 220 try { 221 node.removeNode(); 222 } catch (BackingStoreException e) { 223 IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e); 224 throw new CoreException(status); 225 } 226 removeLoadedNodes(node); 227 } 228 clearNode(Preferences node)229 static void clearNode(Preferences node) throws CoreException { 230 // if the underlying properties file was deleted, clear the values and remove 231 // it from the list of loaded nodes, keep the node as it might still be referenced 232 try { 233 clearAll(node); 234 } catch (BackingStoreException e) { 235 String message = NLS.bind(Messages.preferences_clearNodeException, node.absolutePath()); 236 IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e); 237 throw new CoreException(status); 238 } 239 removeLoadedNodes(node); 240 } 241 clearAll(Preferences node)242 private static void clearAll(Preferences node) throws BackingStoreException { 243 node.clear(); 244 String[] names = node.childrenNames(); 245 for (String name2 : names) { 246 clearAll(node.node(name2)); 247 } 248 } 249 removeLoadedNodes(Preferences node)250 private static void removeLoadedNodes(Preferences node) { 251 String path = node.absolutePath(); 252 synchronized (loadedNodes) { 253 for (Iterator<String> i = loadedNodes.iterator(); i.hasNext();) { 254 String key = i.next(); 255 if (key.startsWith(path)) 256 i.remove(); 257 } 258 } 259 } 260 updatePreferences(IFile file)261 public static void updatePreferences(IFile file) throws CoreException { 262 IPath path = file.getFullPath(); 263 // if we made it this far we are inside /project/.settings and might 264 // have a change to a preference file 265 if (!PREFS_FILE_EXTENSION.equals(path.getFileExtension())) 266 return; 267 268 String project = path.segment(0); 269 String qualifier = path.removeFileExtension().lastSegment(); 270 Preferences root = Platform.getPreferencesService().getRootNode(); 271 Preferences node = root.node(ProjectScope.SCOPE).node(project).node(qualifier); 272 String message = null; 273 try { 274 message = NLS.bind(Messages.preferences_syncException, node.absolutePath()); 275 if (!(node instanceof ProjectPreferences)) 276 return; 277 ProjectPreferences projectPrefs = (ProjectPreferences) node; 278 if (projectPrefs.isWriting) 279 return; 280 read(projectPrefs, file); 281 // Bug 108066: In case the node had existed before it was updated from 282 // file, the read() operation marks it dirty. Override the dirty flag 283 // since we know that the node is expected to be in sync with the file. 284 projectPrefs.dirty = false; 285 286 // make sure that we generate the appropriate resource change events 287 // if encoding settings have changed 288 if (PREFS_REGULAR_QUALIFIER.equals(qualifier) || PREFS_DERIVED_QUALIFIER.equals(qualifier)) 289 preferencesChanged(file.getProject()); 290 } catch (BackingStoreException e) { 291 IStatus status = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e); 292 throw new CoreException(status); 293 } 294 } 295 296 /** 297 * Default constructor. Should only be called by #createExecutableExtension. 298 */ ProjectPreferences()299 public ProjectPreferences() { 300 super(null, null); 301 } 302 ProjectPreferences(EclipsePreferences parent, String name)303 private ProjectPreferences(EclipsePreferences parent, String name) { 304 super(parent, name); 305 306 // cache the segment count 307 String path = absolutePath(); 308 segmentCount = getSegmentCount(path); 309 310 if (segmentCount == 1) 311 return; 312 313 // cache the project name 314 String projectName = getSegment(path, 1); 315 if (projectName != null) 316 project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); 317 318 // cache the qualifier 319 if (segmentCount > 2) 320 qualifier = getSegment(path, 2); 321 } 322 323 @Override childrenNames()324 public String[] childrenNames() throws BackingStoreException { 325 // illegal state if this node has been removed 326 checkRemoved(); 327 initialize(); 328 silentLoad(); 329 return super.childrenNames(); 330 } 331 332 @Override clear()333 public void clear() { 334 // illegal state if this node has been removed 335 checkRemoved(); 336 silentLoad(); 337 super.clear(); 338 } 339 340 /* 341 * Figure out what the children of this node are based on the resources 342 * that are in the workspace. 343 */ computeChildren()344 private String[] computeChildren() { 345 if (project == null) 346 return EMPTY_STRING_ARRAY; 347 IFolder folder = project.getFolder(DEFAULT_PREFERENCES_DIRNAME); 348 if (!folder.exists()) 349 return EMPTY_STRING_ARRAY; 350 IResource[] members = null; 351 try { 352 members = folder.members(); 353 } catch (CoreException e) { 354 return EMPTY_STRING_ARRAY; 355 } 356 ArrayList<String> result = new ArrayList<>(); 357 for (IResource resource : members) { 358 if (resource.getType() == IResource.FILE && PREFS_FILE_EXTENSION.equals(resource.getFullPath().getFileExtension())) 359 result.add(resource.getFullPath().removeFileExtension().lastSegment()); 360 } 361 return result.toArray(EMPTY_STRING_ARRAY); 362 } 363 364 @Override flush()365 public void flush() throws BackingStoreException { 366 if (isReading) 367 return; 368 isWriting = true; 369 try { 370 // call the internal method because we don't want to be synchronized, we will do that ourselves later. 371 IEclipsePreferences toFlush = super.internalFlush(); 372 //if we aren't at the right level, then flush the appropriate node 373 if (toFlush != null) 374 toFlush.flush(); 375 } finally { 376 isWriting = false; 377 } 378 } 379 getFile()380 private IFile getFile() { 381 if (file == null) { 382 if (project == null || qualifier == null) 383 return null; 384 file = getFile(project, qualifier); 385 } 386 return file; 387 } 388 389 /* 390 * Return the node at which these preferences are loaded/saved. 391 */ 392 @Override getLoadLevel()393 protected IEclipsePreferences getLoadLevel() { 394 if (loadLevel == null) { 395 if (project == null || qualifier == null) 396 return null; 397 // Make it relative to this node rather than navigating to it from the root. 398 // Walk backwards up the tree starting at this node. 399 // This is important to avoid a chicken/egg thing on startup. 400 EclipsePreferences node = this; 401 for (int i = 3; i < segmentCount; i++) 402 node = (EclipsePreferences) node.parent(); 403 loadLevel = node; 404 } 405 return loadLevel; 406 } 407 408 /* 409 * Calculate and return the file system location for this preference node. 410 * Use the absolute path of the node to find out the project name so 411 * we can get its location on disk. 412 * 413 * NOTE: we cannot cache the location since it may change over the course 414 * of the project life-cycle. 415 */ 416 @Override getLocation()417 protected IPath getLocation() { 418 if (project == null || qualifier == null) 419 return null; 420 IPath path = project.getLocation(); 421 return computeLocation(path, qualifier); 422 } 423 424 @Override internalCreate(EclipsePreferences nodeParent, String nodeName, Object context)425 protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) { 426 return new ProjectPreferences(nodeParent, nodeName); 427 } 428 429 @Override internalGet(String key)430 protected String internalGet(String key) { 431 // throw NPE if key is null 432 if (key == null) 433 throw new NullPointerException(); 434 // illegal state if this node has been removed 435 checkRemoved(); 436 silentLoad(); 437 return super.internalGet(key); 438 } 439 440 @Override internalPut(String key, String newValue)441 protected String internalPut(String key, String newValue) { 442 // illegal state if this node has been removed 443 checkRemoved(); 444 silentLoad(); 445 if ((segmentCount == 3) && PREFS_REGULAR_QUALIFIER.equals(qualifier) && (project != null)) { 446 if (ResourcesPlugin.PREF_SEPARATE_DERIVED_ENCODINGS.equals(key)) { 447 CharsetManager charsetManager = ((Workspace) ResourcesPlugin.getWorkspace()).getCharsetManager(); 448 if (Boolean.parseBoolean(newValue)) 449 charsetManager.splitEncodingPreferences(project); 450 else 451 charsetManager.mergeEncodingPreferences(project); 452 } 453 } 454 return super.internalPut(key, newValue); 455 } 456 initialize()457 private void initialize() { 458 if (segmentCount != 2) 459 return; 460 461 // if already initialized, then skip this initialization 462 if (initialized) 463 return; 464 465 // initialize the children only if project is opened 466 if (project.isOpen()) { 467 try { 468 synchronized (this) { 469 List<String> addedNames = Arrays.asList(internalChildNames()); 470 String[] names = computeChildren(); 471 // add names only for nodes that were not added previously 472 for (String name : names) { 473 if (!addedNames.contains(name)) { 474 addChild(name, null); 475 } 476 } 477 } 478 } finally { 479 // mark as initialized so that subsequent project opening will not initialize preferences again 480 initialized = true; 481 } 482 } 483 } 484 485 @Override isAlreadyLoaded(IEclipsePreferences node)486 protected boolean isAlreadyLoaded(IEclipsePreferences node) { 487 return loadedNodes.contains(node.absolutePath()); 488 } 489 490 @Override keys()491 public String[] keys() { 492 // illegal state if this node has been removed 493 checkRemoved(); 494 silentLoad(); 495 return super.keys(); 496 } 497 498 @Override load()499 protected void load() throws BackingStoreException { 500 load(true); 501 } 502 load(boolean reportProblems)503 private void load(boolean reportProblems) throws BackingStoreException { 504 IFile localFile = getFile(); 505 if (localFile == null || !localFile.exists()) { 506 if (Policy.DEBUG_PREFERENCES) 507 Policy.debug("Unable to determine preference file or file does not exist for node: " + absolutePath()); //$NON-NLS-1$ 508 return; 509 } 510 if (Policy.DEBUG_PREFERENCES) 511 Policy.debug("Loading preferences from file: " + localFile.getFullPath()); //$NON-NLS-1$ 512 Properties fromDisk = new Properties(); 513 try ( 514 InputStream input = new BufferedInputStream(localFile.getContents(true)); 515 ) { 516 fromDisk.load(input); 517 convertFromProperties(this, fromDisk, true); 518 loadedNodes.add(absolutePath()); 519 } catch (CoreException e) { 520 if (e.getStatus().getCode() == IResourceStatus.RESOURCE_NOT_FOUND) { 521 if (Policy.DEBUG_PREFERENCES) 522 Policy.debug("Preference file does not exist for node: " + absolutePath()); //$NON-NLS-1$ 523 return; 524 } 525 if (reportProblems) { 526 String message = NLS.bind(Messages.preferences_loadException, localFile.getFullPath()); 527 log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e)); 528 throw new BackingStoreException(message); 529 } 530 } catch (IOException e) { 531 if (reportProblems) { 532 String message = NLS.bind(Messages.preferences_loadException, localFile.getFullPath()); 533 log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e)); 534 throw new BackingStoreException(message); 535 } 536 } 537 } 538 539 /** 540 * If we are at the /project node and we are checking for the existence of a child, we 541 * want special behaviour. If the child is a single segment name, then we want to 542 * return true if the node exists OR if a project with that name exists in the workspace. 543 */ 544 @Override nodeExists(String path)545 public boolean nodeExists(String path) throws BackingStoreException { 546 // short circuit for checking this node 547 if (path.length() == 0) 548 return !removed; 549 // illegal state if this node has been removed. 550 // do this AFTER checking for the empty string. 551 checkRemoved(); 552 initialize(); 553 silentLoad(); 554 if (segmentCount != 1) 555 return super.nodeExists(path); 556 if (path.length() == 0) 557 return super.nodeExists(path); 558 if (path.charAt(0) == IPath.SEPARATOR) 559 return super.nodeExists(path); 560 if (path.indexOf(IPath.SEPARATOR) != -1) 561 return super.nodeExists(path); 562 // if we are checking existance of a single segment child of /project, base the answer on 563 // whether or not it exists in the workspace. 564 return ResourcesPlugin.getWorkspace().getRoot().getProject(path).exists() || super.nodeExists(path); 565 } 566 567 @Override remove(String key)568 public void remove(String key) { 569 // illegal state if this node has been removed 570 checkRemoved(); 571 silentLoad(); 572 super.remove(key); 573 if ((segmentCount == 3) && PREFS_REGULAR_QUALIFIER.equals(qualifier) && (project != null)) { 574 if (ResourcesPlugin.PREF_SEPARATE_DERIVED_ENCODINGS.equals(key)) { 575 CharsetManager charsetManager = ((Workspace) ResourcesPlugin.getWorkspace()).getCharsetManager(); 576 if (ResourcesPlugin.DEFAULT_PREF_SEPARATE_DERIVED_ENCODINGS) 577 charsetManager.splitEncodingPreferences(project); 578 else 579 charsetManager.mergeEncodingPreferences(project); 580 } 581 } 582 } 583 584 @Override save()585 protected void save() throws BackingStoreException { 586 final IFile fileInWorkspace = getFile(); 587 if (fileInWorkspace == null) { 588 if (Policy.DEBUG_PREFERENCES) 589 Policy.debug("Not saving preferences since there is no file for node: " + absolutePath()); //$NON-NLS-1$ 590 return; 591 } 592 final String finalQualifier = qualifier; 593 final BackingStoreException[] bse = new BackingStoreException[1]; 594 try { 595 ICoreRunnable operation = monitor -> { 596 try { 597 Properties table = convertToProperties(new SortedProperties(), ""); //$NON-NLS-1$ 598 // nothing to save. delete existing file if one exists. 599 if (table.isEmpty()) { 600 if (fileInWorkspace.exists()) { 601 if (Policy.DEBUG_PREFERENCES) 602 Policy.debug("Deleting preference file: " + fileInWorkspace.getFullPath()); //$NON-NLS-1$ 603 if (fileInWorkspace.isReadOnly()) { 604 IStatus status1 = fileInWorkspace.getWorkspace().validateEdit(new IFile[] {fileInWorkspace}, IWorkspace.VALIDATE_PROMPT); 605 if (!status1.isOK()) 606 throw new CoreException(status1); 607 } 608 try { 609 fileInWorkspace.delete(true, null); 610 } catch (CoreException e1) { 611 String message1 = NLS.bind(Messages.preferences_deleteException, fileInWorkspace.getFullPath()); 612 log(new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IStatus.WARNING, message1, null)); 613 } 614 } 615 return; 616 } 617 table.put(VERSION_KEY, VERSION_VALUE); 618 // print the table to a string and remove the timestamp that Properties#store always adds 619 String s = removeTimestampFromTable(table); 620 String systemLineSeparator = System.lineSeparator(); 621 String fileLineSeparator = FileUtil.getLineSeparator(fileInWorkspace); 622 if (!systemLineSeparator.equals(fileLineSeparator)) 623 s = s.replaceAll(systemLineSeparator, fileLineSeparator); 624 InputStream input = new BufferedInputStream(new ByteArrayInputStream(s.getBytes("UTF-8"))); //$NON-NLS-1$ 625 // make sure that preference folder and file are in sync 626 fileInWorkspace.getParent().refreshLocal(IResource.DEPTH_ZERO, null); 627 fileInWorkspace.refreshLocal(IResource.DEPTH_ZERO, null); 628 if (fileInWorkspace.exists()) { 629 if (Policy.DEBUG_PREFERENCES) 630 Policy.debug("Setting preference file contents for: " + fileInWorkspace.getFullPath()); //$NON-NLS-1$ 631 if (fileInWorkspace.isReadOnly()) { 632 IStatus status2 = fileInWorkspace.getWorkspace().validateEdit(new IFile[] {fileInWorkspace}, IWorkspace.VALIDATE_PROMPT); 633 if (!status2.isOK()) { 634 input.close(); 635 throw new CoreException(status2); 636 } 637 } 638 // set the contents 639 fileInWorkspace.setContents(input, IResource.KEEP_HISTORY, null); 640 } else { 641 // create the file 642 IFolder folder = (IFolder) fileInWorkspace.getParent(); 643 if (!folder.exists()) { 644 if (Policy.DEBUG_PREFERENCES) 645 Policy.debug("Creating parent preference directory: " + folder.getFullPath()); //$NON-NLS-1$ 646 folder.create(IResource.NONE, true, null); 647 } 648 if (Policy.DEBUG_PREFERENCES) 649 Policy.debug("Creating preference file: " + fileInWorkspace.getLocation()); //$NON-NLS-1$ 650 fileInWorkspace.create(input, IResource.NONE, null); 651 } 652 if (PREFS_DERIVED_QUALIFIER.equals(finalQualifier)) 653 fileInWorkspace.setDerived(true, null); 654 } catch (BackingStoreException e2) { 655 bse[0] = e2; 656 } catch (IOException e3) { 657 String message2 = NLS.bind(Messages.preferences_saveProblems, fileInWorkspace.getFullPath()); 658 log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message2, e3)); 659 bse[0] = new BackingStoreException(message2); 660 } 661 }; 662 //don't bother with scheduling rules if we are already inside an operation 663 try { 664 IWorkspace workspace = ResourcesPlugin.getWorkspace(); 665 if (((Workspace) workspace).getWorkManager().isLockAlreadyAcquired()) { 666 operation.run(null); 667 } else { 668 IResourceRuleFactory factory = workspace.getRuleFactory(); 669 // we might: delete the file, create the .settings folder, create the file, modify the file, or set derived flag for the file. 670 ISchedulingRule rule = MultiRule.combine(new ISchedulingRule[] {factory.deleteRule(fileInWorkspace), factory.createRule(fileInWorkspace.getParent()), factory.modifyRule(fileInWorkspace), factory.derivedRule(fileInWorkspace)}); 671 workspace.run(operation, rule, IResource.NONE, null); 672 if (bse[0] != null) 673 throw bse[0]; 674 } 675 } catch (OperationCanceledException e) { 676 throw new BackingStoreException(Messages.preferences_operationCanceled); 677 } 678 } catch (CoreException e) { 679 String message = NLS.bind(Messages.preferences_saveProblems, fileInWorkspace.getFullPath()); 680 log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IStatus.ERROR, message, e)); 681 throw new BackingStoreException(message); 682 } 683 } 684 silentLoad()685 private void silentLoad() { 686 ProjectPreferences node = (ProjectPreferences) getLoadLevel(); 687 if (node == null) 688 return; 689 if (isAlreadyLoaded(node) || node.isLoading()) 690 return; 691 try { 692 node.setLoading(true); 693 node.load(false); 694 } catch (BackingStoreException e) { 695 // will not happen, all exceptions are swallowed by load(false) 696 } finally { 697 node.setLoading(false); 698 } 699 } 700 } 701