|
1 /******************************************************************************* |
|
2 * Copyright (c) 2000, 2008 IBM Corporation and others. |
|
3 * All rights reserved. This program and the accompanying materials |
|
4 * are made available under the terms of the Eclipse Public License v1.0 |
|
5 * which accompanies this distribution, and is available at |
|
6 * http://www.eclipse.org/legal/epl-v10.html |
|
7 * |
|
8 * Contributors: |
|
9 * IBM Corporation - initial API and implementation |
|
10 *******************************************************************************/ |
|
11 package org.eclipse.core.internal.resources; |
|
12 |
|
13 import java.net.URI; |
|
14 import org.eclipse.core.filesystem.*; |
|
15 import org.eclipse.core.filesystem.URIUtil; |
|
16 import org.eclipse.core.internal.localstore.FileSystemResourceManager; |
|
17 import org.eclipse.core.internal.properties.IPropertyManager; |
|
18 import org.eclipse.core.internal.utils.*; |
|
19 import org.eclipse.core.resources.*; |
|
20 import org.eclipse.core.resources.team.IResourceTree; |
|
21 import org.eclipse.core.runtime.*; |
|
22 import org.eclipse.core.runtime.jobs.ILock; |
|
23 import org.eclipse.osgi.util.NLS; |
|
24 |
|
25 /** |
|
26 * @since 2.0 |
|
27 * |
|
28 * Implementation note: Since the move/delete hook involves running third |
|
29 * party code, the workspace lock is not held. This means the workspace |
|
30 * lock must be re-acquired whenever we need to manipulate the workspace |
|
31 * in any way. All entry points from third party code back into the tree must |
|
32 * be done in an acquire/release pair. |
|
33 */ |
|
34 class ResourceTree implements IResourceTree { |
|
35 |
|
36 private boolean isValid = true; |
|
37 private final FileSystemResourceManager localManager; |
|
38 /** |
|
39 * The lock to acquire when the workspace needs to be manipulated |
|
40 */ |
|
41 private ILock lock; |
|
42 private MultiStatus multistatus; |
|
43 private int updateFlags; |
|
44 |
|
45 /** |
|
46 * Constructor for this class. |
|
47 */ |
|
48 public ResourceTree(FileSystemResourceManager localManager, ILock lock, MultiStatus status, int updateFlags) { |
|
49 super(); |
|
50 this.localManager = localManager; |
|
51 this.lock = lock; |
|
52 this.multistatus = status; |
|
53 this.updateFlags = updateFlags; |
|
54 } |
|
55 |
|
56 /** |
|
57 * @see IResourceTree#addToLocalHistory(IFile) |
|
58 */ |
|
59 public void addToLocalHistory(IFile file) { |
|
60 Assert.isLegal(isValid); |
|
61 try { |
|
62 lock.acquire(); |
|
63 if (!file.exists()) |
|
64 return; |
|
65 IFileStore store = localManager.getStore(file); |
|
66 final IFileInfo fileInfo = store.fetchInfo(); |
|
67 if (!fileInfo.exists()) |
|
68 return; |
|
69 localManager.getHistoryStore().addState(file.getFullPath(), store, fileInfo, false); |
|
70 } finally { |
|
71 lock.release(); |
|
72 } |
|
73 } |
|
74 |
|
75 private IFileStore computeDestinationStore(IProjectDescription destDescription) throws CoreException { |
|
76 URI destLocation = destDescription.getLocationURI(); |
|
77 // Use the default area if necessary for the destination. |
|
78 if (destLocation == null) { |
|
79 IPath rootLocation = ResourcesPlugin.getWorkspace().getRoot().getLocation(); |
|
80 destLocation = rootLocation.append(destDescription.getName()).toFile().toURI(); |
|
81 } |
|
82 return EFS.getStore(destLocation); |
|
83 } |
|
84 |
|
85 /** |
|
86 * @see IResourceTree#computeTimestamp(IFile) |
|
87 */ |
|
88 public long computeTimestamp(IFile file) { |
|
89 Assert.isLegal(isValid); |
|
90 try { |
|
91 lock.acquire(); |
|
92 if (!file.getProject().exists()) |
|
93 return NULL_TIMESTAMP; |
|
94 return internalComputeTimestamp(file); |
|
95 } finally { |
|
96 lock.release(); |
|
97 } |
|
98 } |
|
99 |
|
100 /** |
|
101 * Copies the local history of source to destination. Note that if source |
|
102 * is an IFolder, it is assumed that the same structure exists under destination |
|
103 * and the local history of any IFile under source will be copied to the |
|
104 * associated IFile under destination. |
|
105 */ |
|
106 private void copyLocalHistory(IResource source, IResource destination) { |
|
107 localManager.getHistoryStore().copyHistory(source, destination, true); |
|
108 } |
|
109 |
|
110 /** |
|
111 * @see IResourceTree#deletedFile(IFile) |
|
112 */ |
|
113 public void deletedFile(IFile file) { |
|
114 Assert.isLegal(isValid); |
|
115 try { |
|
116 lock.acquire(); |
|
117 // Do nothing if the resource doesn't exist. |
|
118 if (!file.exists()) |
|
119 return; |
|
120 try { |
|
121 // Delete properties, generate marker deltas, and remove the node from the workspace tree. |
|
122 ((Resource) file).deleteResource(true, null); |
|
123 } catch (CoreException e) { |
|
124 String message = NLS.bind(Messages.resources_errorDeleting, file.getFullPath()); |
|
125 IStatus status = new ResourceStatus(IStatus.ERROR, file.getFullPath(), message, e); |
|
126 failed(status); |
|
127 } |
|
128 } finally { |
|
129 lock.release(); |
|
130 } |
|
131 } |
|
132 |
|
133 /** |
|
134 * @see IResourceTree#deletedFolder(IFolder) |
|
135 */ |
|
136 public void deletedFolder(IFolder folder) { |
|
137 Assert.isLegal(isValid); |
|
138 try { |
|
139 lock.acquire(); |
|
140 // Do nothing if the resource doesn't exist. |
|
141 if (!folder.exists()) |
|
142 return; |
|
143 try { |
|
144 // Delete properties, generate marker deltas, and remove the node from the workspace tree. |
|
145 ((Resource) folder).deleteResource(true, null); |
|
146 } catch (CoreException e) { |
|
147 String message = NLS.bind(Messages.resources_errorDeleting, folder.getFullPath()); |
|
148 IStatus status = new ResourceStatus(IStatus.ERROR, folder.getFullPath(), message, e); |
|
149 failed(status); |
|
150 } |
|
151 } finally { |
|
152 lock.release(); |
|
153 } |
|
154 } |
|
155 |
|
156 /** |
|
157 * @see IResourceTree#deletedProject(IProject) |
|
158 */ |
|
159 public void deletedProject(IProject target) { |
|
160 Assert.isLegal(isValid); |
|
161 try { |
|
162 lock.acquire(); |
|
163 // Do nothing if the resource doesn't exist. |
|
164 if (!target.exists()) |
|
165 return; |
|
166 // Delete properties, generate marker deltas, and remove the node from the workspace tree. |
|
167 try { |
|
168 ((Project) target).deleteResource(false, null); |
|
169 } catch (CoreException e) { |
|
170 String message = NLS.bind(Messages.resources_errorDeleting, target.getFullPath()); |
|
171 IStatus status = new ResourceStatus(IStatus.ERROR, target.getFullPath(), message, e); |
|
172 // log the status but don't return until we try and delete the rest of the project info |
|
173 failed(status); |
|
174 } |
|
175 } finally { |
|
176 lock.release(); |
|
177 } |
|
178 } |
|
179 |
|
180 /** |
|
181 * Makes sure that the destination directory for a project move is unoccupied. |
|
182 * Returns true if successful, and false if the move should be aborted |
|
183 */ |
|
184 private boolean ensureDestinationEmpty(IProject source, IFileStore destinationStore, IProgressMonitor monitor) throws CoreException { |
|
185 String message; |
|
186 //Make sure the destination location is unoccupied |
|
187 if (!destinationStore.fetchInfo().exists()) |
|
188 return true; |
|
189 //check for existing children |
|
190 if (destinationStore.childNames(EFS.NONE, Policy.subMonitorFor(monitor, 0)).length > 0) { |
|
191 //allow case rename to proceed |
|
192 if (((Resource) source).getStore().equals(destinationStore)) |
|
193 return true; |
|
194 //fail because the destination is occupied |
|
195 message = NLS.bind(Messages.localstore_resourceExists, destinationStore); |
|
196 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, null); |
|
197 failed(status); |
|
198 return false; |
|
199 } |
|
200 //delete the destination directory to allow for efficient renaming |
|
201 destinationStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, 0)); |
|
202 return true; |
|
203 } |
|
204 |
|
205 /** |
|
206 * This operation has failed for the given reason. Add it to this |
|
207 * resource tree's status. |
|
208 */ |
|
209 public void failed(IStatus reason) { |
|
210 Assert.isLegal(isValid); |
|
211 multistatus.add(reason); |
|
212 } |
|
213 |
|
214 /** |
|
215 * Returns the status object held onto by this resource tree. |
|
216 */ |
|
217 protected IStatus getStatus() { |
|
218 return multistatus; |
|
219 } |
|
220 |
|
221 /** |
|
222 * @see IResourceTree#getTimestamp(IFile) |
|
223 */ |
|
224 public long getTimestamp(IFile file) { |
|
225 Assert.isLegal(isValid); |
|
226 try { |
|
227 lock.acquire(); |
|
228 if (!file.exists()) |
|
229 return NULL_TIMESTAMP; |
|
230 ResourceInfo info = ((File) file).getResourceInfo(false, false); |
|
231 return info == null ? NULL_TIMESTAMP : info.getLocalSyncInfo(); |
|
232 } finally { |
|
233 lock.release(); |
|
234 } |
|
235 } |
|
236 |
|
237 /** |
|
238 * Returns the local timestamp for a file. |
|
239 * |
|
240 * @param file |
|
241 * @return The local file system timestamp |
|
242 */ |
|
243 private long internalComputeTimestamp(IFile file) { |
|
244 IFileInfo fileInfo = localManager.getStore(file).fetchInfo(); |
|
245 return fileInfo.exists() ? fileInfo.getLastModified() : NULL_TIMESTAMP; |
|
246 } |
|
247 |
|
248 /** |
|
249 * Helper method for #standardDeleteFile. Returns a boolean indicating whether or |
|
250 * not the delete was successful. |
|
251 */ |
|
252 private boolean internalDeleteFile(IFile file, int flags, IProgressMonitor monitor) { |
|
253 try { |
|
254 String message = NLS.bind(Messages.resources_deleting, file.getFullPath()); |
|
255 monitor.beginTask(message, Policy.totalWork); |
|
256 Policy.checkCanceled(monitor); |
|
257 |
|
258 // Do nothing if the file doesn't exist in the workspace. |
|
259 if (!file.exists()) { |
|
260 // Indicate that the delete was successful. |
|
261 return true; |
|
262 } |
|
263 // Don't delete contents if this is a linked resource |
|
264 if (file.isLinked()) { |
|
265 deletedFile(file); |
|
266 return true; |
|
267 } |
|
268 // If the file doesn't exist on disk then signal to the workspace to delete the |
|
269 // file and return. |
|
270 IFileStore fileStore = localManager.getStore(file); |
|
271 boolean localExists = fileStore.fetchInfo().exists(); |
|
272 if (!localExists) { |
|
273 deletedFile(file); |
|
274 // Indicate that the delete was successful. |
|
275 return true; |
|
276 } |
|
277 |
|
278 boolean keepHistory = (flags & IResource.KEEP_HISTORY) != 0; |
|
279 boolean force = (flags & IResource.FORCE) != 0; |
|
280 |
|
281 // Add the file to the local history if requested by the user. |
|
282 if (keepHistory) |
|
283 addToLocalHistory(file); |
|
284 monitor.worked(Policy.totalWork / 4); |
|
285 |
|
286 // We want to fail if force is false and the file is not synchronized with the |
|
287 // local file system. |
|
288 if (!force) { |
|
289 boolean inSync = isSynchronized(file, IResource.DEPTH_ZERO); |
|
290 // only want to fail if the file still exists. |
|
291 if (!inSync && localExists) { |
|
292 message = NLS.bind(Messages.localstore_resourceIsOutOfSync, file.getFullPath()); |
|
293 IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, file.getFullPath(), message); |
|
294 failed(status); |
|
295 // Indicate that the delete was unsuccessful. |
|
296 return false; |
|
297 } |
|
298 } |
|
299 monitor.worked(Policy.totalWork / 4); |
|
300 |
|
301 // Try to delete the file from the file system. |
|
302 try { |
|
303 fileStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork / 4)); |
|
304 // If the file was successfully deleted from the file system the |
|
305 // workspace tree should be updated accordingly. |
|
306 deletedFile(file); |
|
307 // Indicate that the delete was successful. |
|
308 return true; |
|
309 } catch (CoreException e) { |
|
310 message = NLS.bind(Messages.resources_couldnotDelete, fileStore.toString()); |
|
311 IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message, e); |
|
312 failed(status); |
|
313 } |
|
314 // Indicate that the delete was unsuccessful. |
|
315 return false; |
|
316 } finally { |
|
317 monitor.done(); |
|
318 } |
|
319 } |
|
320 |
|
321 /** |
|
322 * Helper method for #standardDeleteFolder. Returns a boolean indicating |
|
323 * whether or not the deletion of this folder was successful. Does a best effort |
|
324 * delete of this resource and its children. |
|
325 */ |
|
326 private boolean internalDeleteFolder(IFolder folder, int flags, IProgressMonitor monitor) { |
|
327 String message = NLS.bind(Messages.resources_deleting, folder.getFullPath()); |
|
328 monitor.beginTask("", Policy.totalWork); //$NON-NLS-1$ |
|
329 monitor.subTask(message); |
|
330 Policy.checkCanceled(monitor); |
|
331 |
|
332 // Do nothing if the folder doesn't exist in the workspace. |
|
333 if (!folder.exists()) |
|
334 return true; |
|
335 |
|
336 // Don't delete contents if this is a linked resource |
|
337 if (folder.isLinked()) { |
|
338 deletedFolder(folder); |
|
339 return true; |
|
340 } |
|
341 |
|
342 // If the folder doesn't exist on disk then update the tree and return. |
|
343 IFileStore fileStore = localManager.getStore(folder); |
|
344 if (!fileStore.fetchInfo().exists()) { |
|
345 deletedFolder(folder); |
|
346 return true; |
|
347 } |
|
348 |
|
349 try { |
|
350 //this will delete local and workspace |
|
351 localManager.delete(folder, flags, Policy.subMonitorFor(monitor, Policy.totalWork)); |
|
352 } catch (CoreException ce) { |
|
353 message = NLS.bind(Messages.localstore_couldnotDelete, folder.getFullPath()); |
|
354 IStatus status = new ResourceStatus(IStatus.ERROR, IResourceStatus.FAILED_DELETE_LOCAL, folder.getFullPath(), message, ce); |
|
355 failed(status); |
|
356 return false; |
|
357 } |
|
358 return true; |
|
359 } |
|
360 |
|
361 /** |
|
362 * Does a best-effort delete on this resource and all its children. |
|
363 */ |
|
364 private boolean internalDeleteProject(IProject project, int flags, IProgressMonitor monitor) { |
|
365 // Recursively delete each member of the project. |
|
366 IResource[] members = null; |
|
367 try { |
|
368 members = project.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN); |
|
369 } catch (CoreException e) { |
|
370 String message = NLS.bind(Messages.resources_errorMembers, project.getFullPath()); |
|
371 IStatus status = new ResourceStatus(IStatus.ERROR, project.getFullPath(), message, e); |
|
372 failed(status); |
|
373 // Indicate that the delete was unsuccessful. |
|
374 return false; |
|
375 } |
|
376 boolean deletedChildren = true; |
|
377 for (int i = 0; i < members.length; i++) { |
|
378 IResource child = members[i]; |
|
379 switch (child.getType()) { |
|
380 case IResource.FILE : |
|
381 // ignore the .project file for now and delete it last |
|
382 if (!IProjectDescription.DESCRIPTION_FILE_NAME.equals(child.getName())) |
|
383 deletedChildren &= internalDeleteFile((IFile) child, flags, Policy.subMonitorFor(monitor, Policy.totalWork / members.length)); |
|
384 break; |
|
385 case IResource.FOLDER : |
|
386 deletedChildren &= internalDeleteFolder((IFolder) child, flags, Policy.subMonitorFor(monitor, Policy.totalWork / members.length)); |
|
387 break; |
|
388 } |
|
389 } |
|
390 IFileStore projectStore = localManager.getStore(project); |
|
391 // Check to see if the children were deleted ok. If there was a problem |
|
392 // just return as the problem should have been logged by the recursive |
|
393 // call to the child. |
|
394 if (!deletedChildren) |
|
395 // Indicate that the delete was unsuccessful. |
|
396 return false; |
|
397 |
|
398 //Check if there are any undiscovered children of the project on disk other than description file |
|
399 String[] children; |
|
400 try { |
|
401 children = projectStore.childNames(EFS.NONE, null); |
|
402 } catch (CoreException e) { |
|
403 //treat failure to access the directory as a non-existent directory |
|
404 children = new String[0]; |
|
405 } |
|
406 boolean force = BitMask.isSet(flags, IResource.FORCE); |
|
407 if (!force && (children.length != 1 || !IProjectDescription.DESCRIPTION_FILE_NAME.equals(children[0]))) { |
|
408 String message = NLS.bind(Messages.localstore_resourceIsOutOfSync, project.getName()); |
|
409 failed(new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, project.getFullPath(), message)); |
|
410 return false; |
|
411 } |
|
412 |
|
413 //Now delete the project description file |
|
414 IResource file = project.findMember(IProjectDescription.DESCRIPTION_FILE_NAME); |
|
415 if (file == null) { |
|
416 //the .project have may have been recreated on disk automatically by snapshot |
|
417 IFileStore dotProject = projectStore.getChild(IProjectDescription.DESCRIPTION_FILE_NAME); |
|
418 try { |
|
419 dotProject.delete(EFS.NONE, null); |
|
420 } catch (CoreException e) { |
|
421 failed(e.getStatus()); |
|
422 } |
|
423 } else { |
|
424 boolean deletedProjectFile = internalDeleteFile((IFile) file, flags, Policy.monitorFor(null)); |
|
425 if (!deletedProjectFile) { |
|
426 String message = NLS.bind(Messages.resources_couldnotDelete, file.getFullPath()); |
|
427 IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, file.getFullPath(), message); |
|
428 failed(status); |
|
429 // Indicate that the delete was unsuccessful. |
|
430 return false; |
|
431 } |
|
432 } |
|
433 |
|
434 //children are deleted, so now delete the parent |
|
435 try { |
|
436 projectStore.delete(EFS.NONE, null); |
|
437 deletedProject(project); |
|
438 // Indicate that the delete was successful. |
|
439 return true; |
|
440 } catch (CoreException e) { |
|
441 String message = NLS.bind(Messages.resources_couldnotDelete, projectStore.toString()); |
|
442 IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message, e); |
|
443 failed(status); |
|
444 // Indicate that the delete was unsuccessful. |
|
445 return false; |
|
446 } |
|
447 } |
|
448 |
|
449 /** |
|
450 * Return <code>true</code> if there is a change in the content area for the project. |
|
451 */ |
|
452 private boolean isContentChange(IProject project, IProjectDescription destDescription) { |
|
453 IProjectDescription srcDescription = ((Project) project).internalGetDescription(); |
|
454 URI srcLocation = srcDescription.getLocationURI(); |
|
455 URI destLocation = destDescription.getLocationURI(); |
|
456 if (srcLocation == null || destLocation == null) |
|
457 return true; |
|
458 //don't use URIUtil because we want to treat case rename as a content change |
|
459 return !srcLocation.equals(destLocation); |
|
460 } |
|
461 |
|
462 /** |
|
463 * Return <code>true</code> if there is a change in the name of the project. |
|
464 */ |
|
465 private boolean isNameChange(IProject project, IProjectDescription description) { |
|
466 return !project.getName().equals(description.getName()); |
|
467 } |
|
468 |
|
469 /** |
|
470 * Refreshes the resource hierarchy with its children. In case of failure |
|
471 * adds an appropriate status to the resource tree's status. |
|
472 */ |
|
473 private void safeRefresh(IResource resource) { |
|
474 try { |
|
475 resource.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); |
|
476 } catch (CoreException ce) { |
|
477 IStatus status = new ResourceStatus(IStatus.ERROR, IResourceStatus.FAILED_DELETE_LOCAL, resource.getFullPath(), Messages.refresh_refreshErr, ce); |
|
478 failed(status); |
|
479 } |
|
480 } |
|
481 |
|
482 /** |
|
483 * @see IResourceTree#isSynchronized(IResource, int) |
|
484 */ |
|
485 public boolean isSynchronized(IResource resource, int depth) { |
|
486 try { |
|
487 lock.acquire(); |
|
488 return localManager.isSynchronized(resource, depth); |
|
489 } finally { |
|
490 lock.release(); |
|
491 } |
|
492 } |
|
493 |
|
494 /** |
|
495 * The specific operation for which this tree was created has completed and this tree |
|
496 * should not be used anymore. Ensure that this is the case by making it invalid. This |
|
497 * is checked by all API methods. |
|
498 */ |
|
499 void makeInvalid() { |
|
500 this.isValid = false; |
|
501 } |
|
502 |
|
503 /** |
|
504 * @see IResourceTree#movedFile(IFile, IFile) |
|
505 */ |
|
506 public void movedFile(IFile source, IFile destination) { |
|
507 Assert.isLegal(isValid); |
|
508 try { |
|
509 lock.acquire(); |
|
510 // Do nothing if the resource doesn't exist. |
|
511 if (!source.exists()) |
|
512 return; |
|
513 // If the destination already exists then we have a problem. |
|
514 if (destination.exists()) { |
|
515 String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath()); |
|
516 IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message); |
|
517 // log the status but don't return until we try and move the rest of the resource information. |
|
518 failed(status); |
|
519 } |
|
520 |
|
521 // Move the resource's persistent properties. |
|
522 IPropertyManager propertyManager = ((Resource) source).getPropertyManager(); |
|
523 try { |
|
524 propertyManager.copy(source, destination, IResource.DEPTH_ZERO); |
|
525 propertyManager.deleteProperties(source, IResource.DEPTH_ZERO); |
|
526 } catch (CoreException e) { |
|
527 String message = NLS.bind(Messages.resources_errorPropertiesMove, source.getFullPath(), destination.getFullPath()); |
|
528 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
529 // log the status but don't return until we try and move the rest of the resource information. |
|
530 failed(status); |
|
531 } |
|
532 |
|
533 // Move the node in the workspace tree. |
|
534 Workspace workspace = (Workspace) source.getWorkspace(); |
|
535 try { |
|
536 workspace.move((Resource) source, destination.getFullPath(), IResource.DEPTH_ZERO, updateFlags, false); |
|
537 } catch (CoreException e) { |
|
538 String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath()); |
|
539 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
540 // log the status but don't return until we try and move the rest of the resource information. |
|
541 failed(status); |
|
542 } |
|
543 |
|
544 // Generate the marker deltas. |
|
545 try { |
|
546 workspace.getMarkerManager().moved(source, destination, IResource.DEPTH_ZERO); |
|
547 } catch (CoreException e) { |
|
548 String message = NLS.bind(Messages.resources_errorMarkersDelete, source.getFullPath()); |
|
549 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
550 failed(status); |
|
551 } |
|
552 |
|
553 // Copy the local history information |
|
554 copyLocalHistory(source, destination); |
|
555 } finally { |
|
556 lock.release(); |
|
557 } |
|
558 } |
|
559 |
|
560 /** |
|
561 * @see IResourceTree#movedFolderSubtree(IFolder, IFolder) |
|
562 */ |
|
563 public void movedFolderSubtree(IFolder source, IFolder destination) { |
|
564 Assert.isLegal(isValid); |
|
565 try { |
|
566 lock.acquire(); |
|
567 // Do nothing if the source resource doesn't exist. |
|
568 if (!source.exists()) |
|
569 return; |
|
570 // If the destination already exists then we have an error. |
|
571 if (destination.exists()) { |
|
572 String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath()); |
|
573 IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message); |
|
574 failed(status); |
|
575 return; |
|
576 } |
|
577 |
|
578 // Move the folder properties. |
|
579 int depth = IResource.DEPTH_INFINITE; |
|
580 IPropertyManager propertyManager = ((Resource) source).getPropertyManager(); |
|
581 try { |
|
582 propertyManager.copy(source, destination, depth); |
|
583 propertyManager.deleteProperties(source, depth); |
|
584 } catch (CoreException e) { |
|
585 String message = NLS.bind(Messages.resources_errorPropertiesMove, source.getFullPath(), destination.getFullPath()); |
|
586 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
587 // log the status but don't return until we try and move the rest of the resource info |
|
588 failed(status); |
|
589 } |
|
590 |
|
591 // Create the destination node in the tree. |
|
592 Workspace workspace = (Workspace) source.getWorkspace(); |
|
593 try { |
|
594 workspace.move((Resource) source, destination.getFullPath(), depth, updateFlags, false); |
|
595 } catch (CoreException e) { |
|
596 String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath()); |
|
597 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
598 // log the status but don't return until we try and move the rest of the resource info |
|
599 failed(status); |
|
600 } |
|
601 |
|
602 // Generate the marker deltas. |
|
603 try { |
|
604 workspace.getMarkerManager().moved(source, destination, depth); |
|
605 } catch (CoreException e) { |
|
606 String message = NLS.bind(Messages.resources_errorMarkersDelete, source.getFullPath()); |
|
607 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
608 failed(status); |
|
609 } |
|
610 |
|
611 // Copy the local history for this folder |
|
612 copyLocalHistory(source, destination); |
|
613 } finally { |
|
614 lock.release(); |
|
615 } |
|
616 } |
|
617 |
|
618 /** |
|
619 * @see IResourceTree#movedProjectSubtree(IProject, IProjectDescription) |
|
620 */ |
|
621 public boolean movedProjectSubtree(IProject project, IProjectDescription destDescription) { |
|
622 Assert.isLegal(isValid); |
|
623 try { |
|
624 lock.acquire(); |
|
625 // Do nothing if the source resource doesn't exist. |
|
626 if (!project.exists()) |
|
627 return true; |
|
628 |
|
629 Project source = (Project) project; |
|
630 Project destination = (Project) source.getWorkspace().getRoot().getProject(destDescription.getName()); |
|
631 Workspace workspace = (Workspace) source.getWorkspace(); |
|
632 int depth = IResource.DEPTH_INFINITE; |
|
633 |
|
634 // If the name of the source and destination projects are not the same then |
|
635 // rename the meta area and make changes in the tree. |
|
636 if (isNameChange(source, destDescription)) { |
|
637 if (destination.exists()) { |
|
638 String message = NLS.bind(Messages.resources_mustNotExist, destination.getFullPath()); |
|
639 IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message); |
|
640 failed(status); |
|
641 return false; |
|
642 } |
|
643 |
|
644 // Rename the project metadata area. Close the property store to flush everything to disk |
|
645 try { |
|
646 source.getPropertyManager().closePropertyStore(source); |
|
647 localManager.getHistoryStore().closeHistoryStore(source); |
|
648 } catch (CoreException e) { |
|
649 String message = NLS.bind(Messages.properties_couldNotClose, source.getFullPath()); |
|
650 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
651 // log the status but don't return until we try and move the rest of the resource info |
|
652 failed(status); |
|
653 } |
|
654 final IFileSystem fileSystem = EFS.getLocalFileSystem(); |
|
655 IFileStore oldMetaArea = fileSystem.getStore(workspace.getMetaArea().locationFor(source)); |
|
656 IFileStore newMetaArea = fileSystem.getStore(workspace.getMetaArea().locationFor(destination)); |
|
657 try { |
|
658 oldMetaArea.move(newMetaArea, EFS.NONE, new NullProgressMonitor()); |
|
659 } catch (CoreException e) { |
|
660 String message = NLS.bind(Messages.resources_moveMeta, oldMetaArea, newMetaArea); |
|
661 IStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_METADATA, destination.getFullPath(), message, e); |
|
662 // log the status but don't return until we try and move the rest of the resource info |
|
663 failed(status); |
|
664 } |
|
665 |
|
666 // Move the workspace tree. |
|
667 try { |
|
668 workspace.move(source, destination.getFullPath(), depth, updateFlags, true); |
|
669 } catch (CoreException e) { |
|
670 String message = NLS.bind(Messages.resources_errorMoving, source.getFullPath(), destination.getFullPath()); |
|
671 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
672 // log the status but don't return until we try and move the rest of the resource info |
|
673 failed(status); |
|
674 } |
|
675 |
|
676 // Clear stale state on the destination project. |
|
677 ((ProjectInfo) destination.getResourceInfo(false, true)).fixupAfterMove(); |
|
678 |
|
679 // Generate marker deltas. |
|
680 try { |
|
681 workspace.getMarkerManager().moved(source, destination, depth); |
|
682 } catch (CoreException e) { |
|
683 String message = NLS.bind(Messages.resources_errorMarkersMove, source.getFullPath(), destination.getFullPath()); |
|
684 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
685 // log the status but don't return until we try and move the rest of the resource info |
|
686 failed(status); |
|
687 } |
|
688 // Copy the local history |
|
689 copyLocalHistory(source, destination); |
|
690 } |
|
691 |
|
692 // Write the new project description on the destination project. |
|
693 try { |
|
694 //moving linked resources may have modified the description in memory |
|
695 ((ProjectDescription) destDescription).setLinkDescriptions(destination.internalGetDescription().getLinks()); |
|
696 destination.internalSetDescription(destDescription, true); |
|
697 destination.writeDescription(IResource.FORCE); |
|
698 } catch (CoreException e) { |
|
699 String message = Messages.resources_projectDesc; |
|
700 IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message, e); |
|
701 failed(status); |
|
702 } |
|
703 |
|
704 // write the private project description, including the project location |
|
705 try { |
|
706 workspace.getMetaArea().writePrivateDescription(destination); |
|
707 } catch (CoreException e) { |
|
708 failed(e.getStatus()); |
|
709 } |
|
710 |
|
711 // Do a refresh on the destination project to pick up any newly discovered resources |
|
712 try { |
|
713 destination.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); |
|
714 } catch (CoreException e) { |
|
715 String message = NLS.bind(Messages.resources_errorRefresh, destination.getFullPath()); |
|
716 IStatus status = new ResourceStatus(IStatus.ERROR, destination.getFullPath(), message, e); |
|
717 failed(status); |
|
718 return false; |
|
719 } |
|
720 return true; |
|
721 } finally { |
|
722 lock.release(); |
|
723 } |
|
724 } |
|
725 |
|
726 /** |
|
727 * Helper method for moving the project content. Determines the content location |
|
728 * based on the project description. (default location or user defined?) |
|
729 */ |
|
730 private void moveProjectContent(IProject source, IFileStore destStore, int flags, IProgressMonitor monitor) throws CoreException { |
|
731 try { |
|
732 String message = NLS.bind(Messages.resources_moving, source.getFullPath()); |
|
733 monitor.beginTask(message, 10); |
|
734 IProjectDescription srcDescription = source.getDescription(); |
|
735 URI srcLocation = srcDescription.getLocationURI(); |
|
736 // If the locations are the same (and non-default) then there is nothing to do. |
|
737 if (srcLocation != null && URIUtil.equals(srcLocation, destStore.toURI())) |
|
738 return; |
|
739 |
|
740 //If this is a replace, just make sure the destination location exists, and return |
|
741 boolean replace = (flags & IResource.REPLACE) != 0; |
|
742 if (replace) { |
|
743 destStore.mkdir(EFS.NONE, Policy.subMonitorFor(monitor, 10)); |
|
744 return; |
|
745 } |
|
746 |
|
747 // Move the contents on disk. |
|
748 localManager.move(source, destStore, flags, Policy.subMonitorFor(monitor, 9)); |
|
749 |
|
750 //if this is a deep move, move the contents of any linked resources |
|
751 if ((flags & IResource.SHALLOW) == 0) { |
|
752 IResource[] children = source.members(); |
|
753 for (int i = 0; i < children.length; i++) { |
|
754 if (children[i].isLinked()) { |
|
755 message = NLS.bind(Messages.resources_moving, children[i].getFullPath()); |
|
756 monitor.subTask(message); |
|
757 IFileStore linkDestination = destStore.getChild(children[i].getName()); |
|
758 try { |
|
759 localManager.move(children[i], linkDestination, flags, Policy.monitorFor(null)); |
|
760 } catch (CoreException ce) { |
|
761 //log the failure, but keep trying on remaining links |
|
762 failed(ce.getStatus()); |
|
763 } |
|
764 } |
|
765 } |
|
766 } |
|
767 monitor.worked(1); |
|
768 } finally { |
|
769 monitor.done(); |
|
770 } |
|
771 } |
|
772 |
|
773 /** |
|
774 * @see IResourceTree#standardDeleteFile(IFile, int, IProgressMonitor) |
|
775 */ |
|
776 public void standardDeleteFile(IFile file, int flags, IProgressMonitor monitor) { |
|
777 Assert.isLegal(isValid); |
|
778 try { |
|
779 lock.acquire(); |
|
780 internalDeleteFile(file, flags, monitor); |
|
781 } finally { |
|
782 lock.release(); |
|
783 } |
|
784 } |
|
785 |
|
786 /** |
|
787 * @see IResourceTree#standardDeleteFolder(IFolder, int, IProgressMonitor) |
|
788 */ |
|
789 public void standardDeleteFolder(IFolder folder, int flags, IProgressMonitor monitor) { |
|
790 Assert.isLegal(isValid); |
|
791 try { |
|
792 lock.acquire(); |
|
793 internalDeleteFolder(folder, flags, monitor); |
|
794 } catch (OperationCanceledException oce) { |
|
795 safeRefresh(folder); |
|
796 throw oce; |
|
797 } finally { |
|
798 lock.release(); |
|
799 monitor.done(); |
|
800 } |
|
801 } |
|
802 |
|
803 /** |
|
804 * @see IResourceTree#standardDeleteProject(IProject, int, IProgressMonitor) |
|
805 */ |
|
806 public void standardDeleteProject(IProject project, int flags, IProgressMonitor monitor) { |
|
807 Assert.isLegal(isValid); |
|
808 try { |
|
809 lock.acquire(); |
|
810 String message = NLS.bind(Messages.resources_deleting, project.getFullPath()); |
|
811 monitor.beginTask(message, Policy.totalWork); |
|
812 // Do nothing if the project doesn't exist in the workspace tree. |
|
813 if (!project.exists()) |
|
814 return; |
|
815 |
|
816 boolean alwaysDeleteContent = (flags & IResource.ALWAYS_DELETE_PROJECT_CONTENT) != 0; |
|
817 boolean neverDeleteContent = (flags & IResource.NEVER_DELETE_PROJECT_CONTENT) != 0; |
|
818 boolean success = true; |
|
819 |
|
820 // Delete project content. Don't do anything if the user specified explicitly asked |
|
821 // not to delete the project content or if the project is closed and |
|
822 // ALWAYS_DELETE_PROJECT_CONTENT was not specified. |
|
823 if (alwaysDeleteContent || (project.isOpen() && !neverDeleteContent)) { |
|
824 // Force is implied if alwaysDeleteContent is true or if the project is in sync |
|
825 // with the local file system. |
|
826 if (alwaysDeleteContent || isSynchronized(project, IResource.DEPTH_INFINITE)) { |
|
827 flags |= IResource.FORCE; |
|
828 } |
|
829 |
|
830 // If the project is open we have to recursively try and delete all the files doing best-effort. |
|
831 if (project.isOpen()) { |
|
832 success = internalDeleteProject(project, flags, monitor); |
|
833 if (!success) { |
|
834 IFileStore store = localManager.getStore(project); |
|
835 message = NLS.bind(Messages.resources_couldnotDelete, store.toString()); |
|
836 IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message); |
|
837 failed(status); |
|
838 } |
|
839 return; |
|
840 } |
|
841 |
|
842 // If the project is closed we can short circuit this operation and delete all the files on disk. |
|
843 // The .project file is deleted at the end of the operation. |
|
844 try { |
|
845 IFileStore projectStore = localManager.getStore(project); |
|
846 IFileStore members[] = projectStore.childStores(EFS.NONE, null); |
|
847 for (int i = 0; i < members.length; i++) { |
|
848 if (!IProjectDescription.DESCRIPTION_FILE_NAME.equals(members[i].getName())) |
|
849 members[i].delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork * 7 / 8 / members.length)); |
|
850 } |
|
851 projectStore.delete(EFS.NONE, Policy.subMonitorFor(monitor, Policy.totalWork * 7 / 8 / (members.length > 0 ? members.length : 1))); |
|
852 } catch (OperationCanceledException oce) { |
|
853 safeRefresh(project); |
|
854 throw oce; |
|
855 } catch (CoreException ce) { |
|
856 message = NLS.bind(Messages.localstore_couldnotDelete, project.getFullPath()); |
|
857 IStatus status = new ResourceStatus(IStatus.ERROR, IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message, ce); |
|
858 failed(status); |
|
859 return; |
|
860 } |
|
861 } |
|
862 |
|
863 // Signal that the workspace tree should be updated that the project has been deleted. |
|
864 if (success) |
|
865 deletedProject(project); |
|
866 else { |
|
867 message = NLS.bind(Messages.localstore_couldnotDelete, project.getFullPath()); |
|
868 IStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, project.getFullPath(), message); |
|
869 failed(status); |
|
870 } |
|
871 } finally { |
|
872 lock.release(); |
|
873 monitor.done(); |
|
874 } |
|
875 } |
|
876 |
|
877 /** |
|
878 * @see IResourceTree#standardMoveFile(IFile, IFile, int, IProgressMonitor) |
|
879 */ |
|
880 public void standardMoveFile(IFile source, IFile destination, int flags, IProgressMonitor monitor) { |
|
881 Assert.isLegal(isValid); |
|
882 try { |
|
883 lock.acquire(); |
|
884 String message = NLS.bind(Messages.resources_moving, source.getFullPath()); |
|
885 monitor.subTask(message); |
|
886 |
|
887 // These pre-conditions should all be ok but just in case... |
|
888 if (!source.exists() || destination.exists() || !destination.getParent().isAccessible()) |
|
889 throw new IllegalArgumentException(); |
|
890 |
|
891 boolean force = (flags & IResource.FORCE) != 0; |
|
892 boolean keepHistory = (flags & IResource.KEEP_HISTORY) != 0; |
|
893 boolean isDeep = (flags & IResource.SHALLOW) == 0; |
|
894 |
|
895 // If the file is not in sync with the local file system and force is false, |
|
896 // then signal that we have an error. |
|
897 if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) { |
|
898 message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath()); |
|
899 IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message); |
|
900 failed(status); |
|
901 return; |
|
902 } |
|
903 monitor.worked(Policy.totalWork / 4); |
|
904 |
|
905 // Add the file contents to the local history if requested by the user. |
|
906 if (keepHistory) |
|
907 addToLocalHistory(source); |
|
908 monitor.worked(Policy.totalWork / 4); |
|
909 |
|
910 //for shallow move of linked resources, nothing needs to be moved in the file system |
|
911 if (!isDeep && source.isLinked()) { |
|
912 movedFile(source, destination); |
|
913 return; |
|
914 } |
|
915 |
|
916 // If the file was successfully moved in the file system then the workspace |
|
917 // tree needs to be updated accordingly. Otherwise signal that we have an error. |
|
918 IFileStore destStore = null; |
|
919 boolean failedDeletingSource = false; |
|
920 try { |
|
921 destStore = localManager.getStore(destination); |
|
922 //ensure parent of destination exists |
|
923 destStore.getParent().mkdir(EFS.NONE, Policy.subMonitorFor(monitor, 0)); |
|
924 localManager.move(source, destStore, flags, monitor); |
|
925 } catch (CoreException e) { |
|
926 failed(e.getStatus()); |
|
927 // did the fail occur after copying to the destination? |
|
928 failedDeletingSource = destStore != null && destStore.fetchInfo().exists(); |
|
929 // if so, we should proceed |
|
930 if (!failedDeletingSource) |
|
931 return; |
|
932 } |
|
933 movedFile(source, destination); |
|
934 updateMovedFileTimestamp(destination, internalComputeTimestamp(destination)); |
|
935 if (failedDeletingSource) { |
|
936 //recreate source file to ensure we are not out of sync |
|
937 try { |
|
938 source.refreshLocal(IResource.DEPTH_INFINITE, null); |
|
939 } catch (CoreException e) { |
|
940 //ignore secondary failure - we have already logged the main failure |
|
941 } |
|
942 } |
|
943 monitor.worked(Policy.totalWork / 4); |
|
944 return; |
|
945 } finally { |
|
946 lock.release(); |
|
947 monitor.done(); |
|
948 } |
|
949 } |
|
950 |
|
951 /** |
|
952 * @see IResourceTree#standardMoveFolder(IFolder, IFolder, int, IProgressMonitor) |
|
953 */ |
|
954 public void standardMoveFolder(IFolder source, IFolder destination, int flags, IProgressMonitor monitor) { |
|
955 Assert.isLegal(isValid); |
|
956 try { |
|
957 lock.acquire(); |
|
958 String message = NLS.bind(Messages.resources_moving, source.getFullPath()); |
|
959 monitor.beginTask(message, 100); |
|
960 |
|
961 // These pre-conditions should all be ok but just in case... |
|
962 if (!source.exists() || destination.exists() || !destination.getParent().isAccessible()) |
|
963 throw new IllegalArgumentException(); |
|
964 |
|
965 // Check to see if we are synchronized with the local file system. If we are in sync then we can |
|
966 // short circuit this method and do a file system only move. Otherwise we have to recursively |
|
967 // try and move all resources, doing it in a best-effort manner. |
|
968 boolean force = (flags & IResource.FORCE) != 0; |
|
969 if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) { |
|
970 message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath()); |
|
971 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message); |
|
972 failed(status); |
|
973 return; |
|
974 } |
|
975 monitor.worked(20); |
|
976 |
|
977 //for linked resources, nothing needs to be moved in the file system |
|
978 boolean isDeep = (flags & IResource.SHALLOW) == 0; |
|
979 if (!isDeep && source.isLinked()) { |
|
980 movedFolderSubtree(source, destination); |
|
981 return; |
|
982 } |
|
983 |
|
984 // Move the resources in the file system. Only the FORCE flag is valid here so don't |
|
985 // have to worry about clearing the KEEP_HISTORY flag. |
|
986 IFileStore destStore = null; |
|
987 boolean failedDeletingSource = false; |
|
988 try { |
|
989 destStore = localManager.getStore(destination); |
|
990 localManager.move(source, destStore, flags, Policy.subMonitorFor(monitor, 60)); |
|
991 } catch (CoreException e) { |
|
992 failed(e.getStatus()); |
|
993 // did the fail occur after copying to the destination? |
|
994 failedDeletingSource = destStore != null && destStore.fetchInfo().exists(); |
|
995 // if so, we should proceed |
|
996 if (!failedDeletingSource) |
|
997 return; |
|
998 } |
|
999 movedFolderSubtree(source, destination); |
|
1000 monitor.worked(20); |
|
1001 updateTimestamps(destination, isDeep); |
|
1002 if (failedDeletingSource) { |
|
1003 //the move could have been partially successful, so refresh to ensure we are in sync |
|
1004 try { |
|
1005 source.refreshLocal(IResource.DEPTH_INFINITE, null); |
|
1006 destination.refreshLocal(IResource.DEPTH_INFINITE, null); |
|
1007 } catch (CoreException e) { |
|
1008 //ignore secondary failures -we have already logged main failure |
|
1009 } |
|
1010 } |
|
1011 } finally { |
|
1012 lock.release(); |
|
1013 monitor.done(); |
|
1014 } |
|
1015 } |
|
1016 |
|
1017 /** |
|
1018 * @see IResourceTree#standardMoveProject(IProject, IProjectDescription, int, IProgressMonitor) |
|
1019 */ |
|
1020 public void standardMoveProject(IProject source, IProjectDescription description, int flags, IProgressMonitor monitor) { |
|
1021 Assert.isLegal(isValid); |
|
1022 try { |
|
1023 lock.acquire(); |
|
1024 String message = NLS.bind(Messages.resources_moving, source.getFullPath()); |
|
1025 monitor.beginTask(message, Policy.totalWork); |
|
1026 |
|
1027 // Double-check this pre-condition. |
|
1028 if (!source.isAccessible()) |
|
1029 throw new IllegalArgumentException(); |
|
1030 |
|
1031 // If there is nothing to do on disk then signal to make the workspace tree |
|
1032 // changes. |
|
1033 if (!isContentChange(source, description)) { |
|
1034 movedProjectSubtree(source, description); |
|
1035 return; |
|
1036 } |
|
1037 |
|
1038 // Check to see if we are synchronized with the local file system. |
|
1039 boolean force = (flags & IResource.FORCE) != 0; |
|
1040 if (!force && !isSynchronized(source, IResource.DEPTH_INFINITE)) { |
|
1041 // FIXME: make this a best effort move? |
|
1042 message = NLS.bind(Messages.localstore_resourceIsOutOfSync, source.getFullPath()); |
|
1043 IStatus status = new ResourceStatus(IResourceStatus.OUT_OF_SYNC_LOCAL, source.getFullPath(), message); |
|
1044 failed(status); |
|
1045 return; |
|
1046 } |
|
1047 |
|
1048 IFileStore destinationStore; |
|
1049 try { |
|
1050 destinationStore = computeDestinationStore(description); |
|
1051 //destination can be non-empty on replace |
|
1052 if ((flags & IResource.REPLACE) == 0) |
|
1053 if (!ensureDestinationEmpty(source, destinationStore, monitor)) |
|
1054 return; |
|
1055 } catch (CoreException e) { |
|
1056 //must fail if the destination location cannot be accessd (undefined file system) |
|
1057 message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath()); |
|
1058 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
1059 failed(status); |
|
1060 return; |
|
1061 } |
|
1062 |
|
1063 // Move the project content in the local file system. |
|
1064 try { |
|
1065 moveProjectContent(source, destinationStore, flags, Policy.subMonitorFor(monitor, Policy.totalWork * 3 / 4)); |
|
1066 } catch (CoreException e) { |
|
1067 message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath()); |
|
1068 IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e); |
|
1069 failed(status); |
|
1070 //refresh the project because it might have been partially moved |
|
1071 try { |
|
1072 source.refreshLocal(IResource.DEPTH_INFINITE, null); |
|
1073 } catch (CoreException e2) { |
|
1074 //ignore secondary failures |
|
1075 } |
|
1076 } |
|
1077 |
|
1078 // If we got this far the project content has been moved on disk (if necessary) |
|
1079 // and we need to update the workspace tree. |
|
1080 movedProjectSubtree(source, description); |
|
1081 monitor.worked(Policy.totalWork * 1 / 8); |
|
1082 |
|
1083 boolean isDeep = (flags & IResource.SHALLOW) == 0; |
|
1084 updateTimestamps(source.getWorkspace().getRoot().getProject(description.getName()), isDeep); |
|
1085 monitor.worked(Policy.totalWork * 1 / 8); |
|
1086 } finally { |
|
1087 lock.release(); |
|
1088 monitor.done(); |
|
1089 } |
|
1090 } |
|
1091 |
|
1092 /** |
|
1093 * @see IResourceTree#updateMovedFileTimestamp(IFile, long) |
|
1094 */ |
|
1095 public void updateMovedFileTimestamp(IFile file, long timestamp) { |
|
1096 Assert.isLegal(isValid); |
|
1097 try { |
|
1098 lock.acquire(); |
|
1099 // Do nothing if the file doesn't exist in the workspace tree. |
|
1100 if (!file.exists()) |
|
1101 return; |
|
1102 // Update the timestamp in the tree. |
|
1103 ResourceInfo info = ((Resource) file).getResourceInfo(false, true); |
|
1104 // The info should never be null since we just checked that the resource exists in the tree. |
|
1105 localManager.updateLocalSync(info, timestamp); |
|
1106 //remove the linked bit since this resource has been moved in the file system |
|
1107 info.clear(ICoreConstants.M_LINK); |
|
1108 } finally { |
|
1109 lock.release(); |
|
1110 } |
|
1111 } |
|
1112 |
|
1113 /** |
|
1114 * Helper method to update all the timestamps in the tree to match |
|
1115 * those in the file system. Used after a #move. |
|
1116 */ |
|
1117 private void updateTimestamps(IResource root, final boolean isDeep) { |
|
1118 IResourceVisitor visitor = new IResourceVisitor() { |
|
1119 public boolean visit(IResource resource) { |
|
1120 if (resource.isLinked()) { |
|
1121 if (isDeep) { |
|
1122 //clear the linked resource bit, if any |
|
1123 ResourceInfo info = ((Resource) resource).getResourceInfo(false, true); |
|
1124 info.clear(ICoreConstants.M_LINK); |
|
1125 } |
|
1126 return true; |
|
1127 } |
|
1128 //only needed if underlying file system does not preserve timestamps |
|
1129 // if (resource.getType() == IResource.FILE) { |
|
1130 // IFile file = (IFile) resource; |
|
1131 // updateMovedFileTimestamp(file, computeTimestamp(file)); |
|
1132 // } |
|
1133 return true; |
|
1134 } |
|
1135 }; |
|
1136 try { |
|
1137 root.accept(visitor, IResource.DEPTH_INFINITE, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS | IContainer.INCLUDE_HIDDEN); |
|
1138 } catch (CoreException e) { |
|
1139 // No exception should be thrown. |
|
1140 } |
|
1141 } |
|
1142 } |