platform-core-home/downloads/examples/movedeletehook/MoveDeleteHookExample1.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (view) (download)

1 : dj 1.1 /*******************************************************************************
2 :     * Copyright (c) 2002 International Business Machines Corp. and others.
3 :     * All rights reserved. This program and the accompanying materials
4 :     * are made available under the terms of the Common Public License v0.5
5 :     * which accompanies this distribution, and is available at
6 :     * http://www.eclipse.org/legal/cpl-v05.html
7 :     *
8 :     * Contributors:
9 :     * IBM Corporation - initial implementation
10 :     ******************************************************************************/
11 :     package com.example.movedeletehook;
12 :    
13 :     import java.io.BufferedInputStream;
14 :     import java.io.BufferedOutputStream;
15 :     import java.io.FileInputStream;
16 :     import java.io.FileNotFoundException;
17 :     import java.io.FileOutputStream;
18 :     import java.io.IOException;
19 :     import java.io.InputStream;
20 :     import java.io.OutputStream;
21 :    
22 :     import org.eclipse.core.resources.IContainer;
23 :     import org.eclipse.core.resources.IFile;
24 :     import org.eclipse.core.resources.IFolder;
25 :     import org.eclipse.core.resources.IProject;
26 :     import org.eclipse.core.resources.IProjectDescription;
27 :     import org.eclipse.core.resources.IResource;
28 :     import org.eclipse.core.resources.IResourceVisitor;
29 :     import org.eclipse.core.resources.team.IMoveDeleteHook;
30 :     import org.eclipse.core.resources.team.IResourceTree;
31 :     import org.eclipse.core.runtime.CoreException;
32 :     import org.eclipse.core.runtime.IPath;
33 :     import org.eclipse.core.runtime.IProgressMonitor;
34 :     import org.eclipse.core.runtime.Platform;
35 :     import org.eclipse.core.runtime.Status;
36 :    
37 :     /**
38 :     * Example implementation of <code>IMoveDeleteHook</code> illustrating
39 :     * how this hook should be used.
40 :     * <p>
41 :     * A word on terminology. The code in this hook lives in a world where it
42 :     * must deal with two parallel notions of "file" that must not be confused.
43 :     * The terminology we use to keep these straight:
44 :     * <ul>
45 :     * <li><b>local file system</b> - We use "local file system" (abbreviated "lfs")
46 :     * when talking about files and folders (directories) on disk; these are
47 :     * accessed through <code>java.io.File</code> protocol.
48 :     * </li>
49 :     * <li><b>workspace resource tree</b> - We use "workspace resource tree"
50 :     * when talking about in the Eclipse Platform's in-memory representation of
51 :     * files and folders; these are ordinarily accessed through
52 :     * <code>org.eclipse.core.resources.IResource</code> protocol.
53 :     * </li>
54 :     * </ul>
55 :     * The general game being played is simple: The hook implementation deals
56 :     * directly with the files in the local file system using whatever means it
57 :     * has at its disposal, and then directs the Eclipse platform how to update the
58 :     * workspace resource tree to match using methods on the
59 :     * <code>org.eclipse.core.resources.team.IResourceTree</code>
60 :     * object passed to the hook on each occasion.
61 :     * </p>
62 :     * <p>
63 :     * N.B. It is important for all core methods to update the progress monitor so
64 :     * that the user knows that long-running operations are making progress
65 :     * (and can cancel if required). Progress monitoring was omitted here only
66 :     * because it made the code more difficult to read without shedding much light
67 :     * on how to use this hook correctly. Please do not follow our bad example.
68 :     * </p>
69 :     *
70 :     * @see org.eclipse.core.resources.IResource
71 :     * @see org.eclipse.core.resources.team.IMoveDeleteHook
72 :     * @see org.eclipse.core.resources.team.IResourceTree
73 :     */
74 :     public class MoveDeleteHookExample1 implements IMoveDeleteHook {
75 :    
76 :     /**
77 :     * Creates a new hook instance.
78 :     */
79 :     public MoveDeleteHookExample1() {
80 :     }
81 :    
82 :     /**
83 :     * This <code>IMoveDeleteHook</code> method implements
84 :     * <code>IResource.delete(int,IProgressMonitor)</code> where the receiver is
85 :     * a file. This example implementation illustrates the steps involved
86 :     * (except for progress monitoring).
87 :     *
88 :     * @param tree the workspace resource tree; this object is only valid
89 :     * for the duration of the invocation of this method, and must not be
90 :     * used after this method has completed
91 :     * @param file the handle of the file to delete; the receiver of
92 :     * <code>IResource.delete(int,IProgressMonitor)</code>
93 :     * @param updateFlags bit-wise or of update flag constants as per
94 :     * <code>IResource.delete(int,IProgressMonitor)</code>
95 :     * @param monitor the progress monitor, or <code>null</code> as per
96 :     * <code>IResource.delete(int,IProgressMonitor)</code>
97 :     * @return <code>false</code> if this method declined to assume
98 :     * responsibility for this operation, and <code>true</code> if this method
99 :     * attempted to carry out the operation
100 :     * @see IResource#delete(int,IProgressMonitor)
101 :     * @see IMoveDeleteHook#deleteFile(IResourceTree,IFile,int,IProgressMonitor)
102 :     */
103 :     public boolean deleteFile(
104 :     IResourceTree tree,
105 :     IFile file,
106 :     int updateFlags,
107 :     IProgressMonitor monitor) {
108 :    
109 :     // do nothing if file does not exist in the workspace resource tree
110 :     if (!file.exists()) {
111 :     // return true anyway to say that the operation has been done
112 :     return true;
113 :     }
114 :    
115 :     // succeed immediately if file does not exist in the local file system
116 :     java.io.File lfsFile = file.getLocation().toFile();
117 :     if (!lfsFile.exists()) {
118 :     // update the workspace resource tree to match
119 :     tree.deletedFile(file);
120 :     // return true to say that the operation has been done
121 :     return true;
122 :     }
123 :    
124 :     // if FORCE is not specified, fail if the workspace resource tree is
125 :     // not in sync with file in the local file system
126 :     boolean force = (updateFlags & IResource.FORCE) != 0;
127 :     if (!force) {
128 :     boolean inSync = tree.isSynchronized(file, IResource.DEPTH_ZERO);
129 :     if (!inSync) {
130 :     // report failure
131 :     Status status =
132 :     new Status(
133 :     Status.ERROR,
134 :     "com.example.movedeletehook",
135 :     0,
136 :     "File " + file.getFullPath() + " is out of sync with the local file system",
137 :     null);
138 :     tree.failed(status);
139 :     // return true to say that the operation has been done
140 :     return true;
141 :     }
142 :     }
143 :    
144 :     // capture the current state of the file in the local history if
145 :     // KEEP_HISTORY is specified
146 :     boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 0;
147 :     if (keepHistory) {
148 :     tree.addToLocalHistory(file);
149 :     }
150 :    
151 :     // try to delete the file from the local file system
152 :     boolean success = lfsFile.delete();
153 :     if (success) {
154 :     // update the workspace resource tree to match
155 :     tree.deletedFile(file);
156 :     // return true to say that the operation has been done
157 :     return true;
158 :     } else {
159 :     // report an unexpected failure
160 :     Status status =
161 :     new Status(
162 :     Status.ERROR,
163 :     "com.example.movedeletehook",
164 :     0,
165 :     "Unable to delete file " + file.getFullPath() + " from the local file system",
166 :     null);
167 :     tree.failed(status);
168 :     // return true to say that the operation has been done
169 :     return true;
170 :     }
171 :     }
172 :    
173 :     /**
174 :     * This <code>IMoveDeleteHook</code> method implements
175 :     * <code>IResource.delete(int,IProgressMonitor)</code> where the receiver is
176 :     * a folder. This example implementation illustrates the steps involved
177 :     * (except for progress monitoring). The general approach illustrated here
178 :     * first deletes the entire subtree in the local file system and then fixes
179 :     * up the workspace resource tree to match. (There other approach is to
180 :     * carry out the deletion in step.)
181 :     *
182 :     * @param tree the workspace resource tree; this object is only valid
183 :     * for the duration of the invocation of this method, and must not be
184 :     * used after this method has completed
185 :     * @param folder the handle of the folder to delete; the receiver of
186 :     * <code>IResource.delete(int,IProgressMonitor)</code>
187 :     * @param updateFlags bit-wise or of update flag constants as per
188 :     * <code>IResource.delete(int,IProgressMonitor)</code>
189 :     * @param monitor the progress monitor, or <code>null</code> as per
190 :     * <code>IResource.delete(int,IProgressMonitor)</code>
191 :     * @return <code>false</code> if this method declined to assume
192 :     * responsibility for this operation, and <code>true</code> if this
193 :     * method attempted to carry out the operation
194 :     * @see IResource#delete(int,IProgressMonitor)
195 :     * @see IMoveDeleteHook#deleteFolder(IResourceTree,IFolder,int,IProgressMonitor)
196 :     */
197 :     public boolean deleteFolder(
198 :     IResourceTree tree,
199 :     IFolder folder,
200 :     int updateFlags,
201 :     IProgressMonitor monitor) {
202 :    
203 :     // do nothing if folder does not exist in the workspace resource tree
204 :     if (!folder.exists()) {
205 :     // return true to say that the operation has been done
206 :     return true;
207 :     }
208 :    
209 :     // succeed immediately if folder does not exist in the local file system
210 :     java.io.File lfsFolder = folder.getLocation().toFile();
211 :     if (!lfsFolder.exists()) {
212 :     // update the workspace resource tree to match
213 :     tree.deletedFolder(folder);
214 :     // return true to say that the operation has been done
215 :     return true;
216 :     }
217 :    
218 :     // if FORCE is not specified, fail if the workspace resource tree is
219 :     // not in sync with the directory subtree in the local file
220 :     // system (depth infinity)
221 :     boolean force = (updateFlags & IResource.FORCE) != 0;
222 :     if (!force) {
223 :     boolean inSync = tree.isSynchronized(folder, IResource.DEPTH_INFINITE);
224 :     if (!inSync) {
225 :     // report failure
226 :     Status status =
227 :     new Status(
228 :     Status.ERROR,
229 :     "com.example.movedeletehook",
230 :     0,
231 :     "Folder "
232 :     + folder.getFullPath()
233 :     + " or one of its descendents is out of sync with the local file system",
234 :     null);
235 :     tree.failed(status);
236 :     // return true to say that the operation has been done
237 :     return true;
238 :     }
239 :     }
240 :    
241 :     // capture the current state of all files in the local history if
242 :     // KEEP_HISTORY is specified
243 :     boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 0;
244 :     if (keepHistory) {
245 :     addAllFilesToHistory(tree, folder);
246 :     }
247 :    
248 :     // try to delete the subtree in the local file system
249 :     boolean lfsSuccess = deleteLocalSubtree(lfsFolder);
250 :     if (lfsSuccess) {
251 :     // update the workspace resource tree to match
252 :     tree.deletedFolder(folder);
253 :     // return true to say that the operation has been done
254 :     return true;
255 :     } else {
256 :     // report an unexpected failure
257 :     Status status =
258 :     new Status(
259 :     Status.ERROR,
260 :     "com.example.movedeletehook",
261 :     0,
262 :     "Unable to delete folder "
263 :     + folder.getFullPath()
264 :     + " from the local file system",
265 :     null);
266 :     tree.failed(status);
267 :     // prune out any resources in the workspace resource tree that are gone
268 :     pruneMissingResources(tree, folder);
269 :     // return true to say that the operation has been done
270 :     return true;
271 :     }
272 :     }
273 :    
274 :     /**
275 :     * Deletes resources from the workspace resource tree below the given
276 :     * container if they do not exist in the local file system. The given
277 :     * container itself is not affected.
278 :     *
279 :     * @param tree the workspace resource tree
280 :     * @param container the root container (a folder or project)
281 :     */
282 :     private void pruneMissingResources(IResourceTree tree, IContainer container) {
283 :     IResource[] children;
284 :     try {
285 :     // we're interested in all members, including team-private ones
286 :     children = container.members(IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
287 :     } catch (CoreException e) {
288 :     // bail quietly if there are problem with accessing its members
289 :     return;
290 :     }
291 :     // iterate over children looking for ones to prune
292 :     for (int i = 0; i < children.length; i++) {
293 :     IResource child = children[i];
294 :     // does child exist in the local file system?
295 :     java.io.File lfsChild = child.getLocation().toFile();
296 :     if (!lfsChild.exists()) {
297 :     // child no longer exists in local file system
298 :     switch (child.getType()) {
299 :     case IResource.FILE :
300 :     // update workspace resource tree to say we deleted it
301 :     tree.deletedFile((IFile) child);
302 :     break;
303 :     case IResource.FOLDER :
304 :     // update workspace resource tree to say we deleted it
305 :     tree.deletedFolder((IFolder) child);
306 :     break;
307 :     case IResource.PROJECT :
308 :     // can't happen since container is a folder or project
309 :     break;
310 :     case IResource.ROOT :
311 :     // can't happen since container is a folder or project
312 :     break;
313 :     }
314 :     } else {
315 :     // child exists in local file system
316 :     if (child.getType() == IResource.FOLDER) {
317 :     // recurse to prune inside a child subfolder
318 :     pruneMissingResources(tree, (IFolder) child);
319 :     }
320 :     }
321 :     }
322 :     }
323 :    
324 :     /**
325 :     * This <code>IMoveDeleteHook</code> method implements
326 :     * <code>IResource.delete(int,IProgressMonitor)</code> where the receiver is
327 :     * a project. This example implementation illustrates the steps involved
328 :     * (except for progress monitoring).
329 :     *
330 :     * @param tree the workspace resource tree; this object is only valid
331 :     * for the duration of the invocation of this method, and must not be
332 :     * used after this method has completed
333 :     * @param project the handle of the project to delete; the receiver of
334 :     * <code>IResource.delete(int,IProgressMonitor)</code>
335 :     * @param updateFlags bit-wise or of update flag constants as per
336 :     * <code>IResource.delete(int,IProgressMonitor)</code>
337 :     * @param monitor the progress monitor, or <code>null</code> as per
338 :     * <code>IResource.delete(int,IProgressMonitor)</code>
339 :     * @return <code>false</code> if this method declined to assume
340 :     * responsibility for this operation, and <code>true</code> if this
341 :     * method attempted to carry out the operation
342 :     * @see IResource#delete(int,IProgressMonitor)
343 :     * @see IMoveDeleteHook#deleteProject(IResourceTree, IProject, int, IProgressMonitor)
344 :     */
345 :     public boolean deleteProject(
346 :     IResourceTree tree,
347 :     IProject project,
348 :     int updateFlags,
349 :     IProgressMonitor monitor) {
350 :    
351 :     // do nothing if the project does not exist in the workspace resource tree
352 :     if (!project.exists()) {
353 :     // return true to say that the operation has been done
354 :     return true;
355 :     }
356 :    
357 :     // succeed immediately if project content area does not exist in the local file system
358 :     java.io.File lfsProjectContentArea = project.getLocation().toFile();
359 :     if (!lfsProjectContentArea.exists()) {
360 :     // update the workspace resource tree to match
361 :     tree.deletedProject(project);
362 :     // return true to say that the operation has been done
363 :     return true;
364 :     }
365 :    
366 :     // decide if files in project content area are supposed to get deleted
367 :     boolean alwaysDelete =
368 :     (updateFlags & IResource.ALWAYS_DELETE_PROJECT_CONTENT) != 0;
369 :     boolean neverDelete =
370 :     (updateFlags & IResource.NEVER_DELETE_PROJECT_CONTENT) != 0;
371 :     boolean force = (updateFlags & IResource.FORCE) != 0;
372 :     boolean deletingContent;
373 :     if (neverDelete) {
374 :     deletingContent = false;
375 :     } else if (alwaysDelete) {
376 :     // ALWAYS_DELETE_PROJECT_CONTENT implies FORCE
377 :     force = true;
378 :     deletingContent = true;
379 :     } else {
380 :     // default: delete content area for open projects but not closed ones
381 :     deletingContent = project.isOpen();
382 :     }
383 :    
384 :     // if deleting the project content area and FORCE is not specified
385 :     // (and ALWAYS_DELETE_PROJECT_CONTENT, which implies FORCE)
386 :     // fail if the workspace resource tree is not in sync with the
387 :     // directory subtree in the local file system (depth infinity)
388 :     if (deletingContent & !force) {
389 :     boolean inSync = tree.isSynchronized(project, IResource.DEPTH_INFINITE);
390 :     if (!inSync) {
391 :     // report failure
392 :     Status status =
393 :     new Status(
394 :     Status.ERROR,
395 :     "com.example.movedeletehook",
396 :     0,
397 :     "Project "
398 :     + project.getFullPath()
399 :     + " is out of sync with the local file system",
400 :     null);
401 :     tree.failed(status);
402 :     // return true to say that the operation has been done
403 :     return true;
404 :     }
405 :     }
406 :    
407 :     // n.b. never capture local history when a project is being deleted
408 :     // because history is kept with the project and gets deleted too
409 :    
410 :     if (!deletingContent) {
411 :     // delete the project from the workspace resource tree
412 :     tree.deletedProject(project);
413 :     // return true to say that the operation has been done
414 :     return true;
415 :     }
416 :    
417 :     // try to delete the project content area in the local file system
418 :     boolean lfsSuccess = deleteLocalSubtree(lfsProjectContentArea);
419 :     if (lfsSuccess) {
420 :     // if successful delete the project from the workspace resource tree
421 :     tree.deletedProject(project);
422 :     // return true to say that the operation has been done
423 :     return true;
424 :     } else {
425 :     // report an unexpected failure
426 :     Status status =
427 :     new Status(
428 :     Status.ERROR,
429 :     "com.example.movedeletehook",
430 :     0,
431 :     "Unable to delete project "
432 :     + project.getFullPath()
433 :     + " from the local file system",
434 :     null);
435 :     tree.failed(status);
436 :     // prune out any resources in the workspace resource tree that are gone
437 :     pruneMissingResources(tree, project);
438 :     // return true to say that the operation has been done
439 :     return true;
440 :     }
441 :     }
442 :    
443 :     /**
444 :     * This <code>IMoveDeleteHook</code> method implements
445 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code> where the receiver
446 :     * is a file. This example implementation illustrates the steps involved
447 :     * (except for progress monitoring).
448 :     *
449 :     * @param tree the workspace resource tree; this object is only valid
450 :     * for the duration of the invocation of this method, and must not be
451 :     * used after this method has completed
452 :     * @param source the handle of the file to move; the receiver of
453 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>;
454 :     * guaranteed to exist in the workspace resource tree
455 :     * @param destination the handle of where the file will move to; the handle
456 :     * equivalent of the first parameter to
457 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>;
458 :     * guaranteed to not exist in the workspace resource tree;
459 :     * parent container guaranteed to exist and be open
460 :     * @param updateFlags bit-wise or of update flag constants as per
461 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>
462 :     * @param monitor the progress monitor, or <code>null</code> as per
463 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>
464 :     * @return <code>false</code> if this method declined to assume
465 :     * responsibility for this operation, and <code>true</code> if this
466 :     * method attempted to carry out the operation
467 :     * @see IResource#move(IPath,int,IProgressMonitor)
468 :     * @see IMoveDeleteHook#moveFile(IResourceTree,IFile,IFile,int,IProgressMonitor)
469 :     */
470 :     public boolean moveFile(
471 :     IResourceTree tree,
472 :     IFile source,
473 :     IFile destination,
474 :     int updateFlags,
475 :     IProgressMonitor monitor) {
476 :    
477 :     // Given: source exists in workspace resource tree
478 :     // Given: destination does not exist in workspace resource tree
479 :     // Given: destination parent exists and is open in workspace resource tree
480 :     if (!source.exists()
481 :     || destination.exists()
482 :     || !destination.getParent().isAccessible()) {
483 :     throw new IllegalArgumentException();
484 :     }
485 :    
486 :     // if FORCE is not specified, fail if the workspace resource tree is
487 :     // not in sync at source file in the local file system
488 :     boolean force = (updateFlags & IResource.FORCE) != 0;
489 :     if (!force) {
490 :     boolean inSync = tree.isSynchronized(source, IResource.DEPTH_ZERO);
491 :     if (!inSync) {
492 :     // report failure
493 :     Status status =
494 :     new Status(
495 :     Status.ERROR,
496 :     "com.example.movedeletehook",
497 :     0,
498 :     "File " + source.getFullPath() + " is out of sync with the local file system",
499 :     null);
500 :     tree.failed(status);
501 :     // return true to say that the operation has been done
502 :     return true;
503 :     }
504 :     }
505 :    
506 :     // capture the current state of the source file in the local history if
507 :     // KEEP_HISTORY is specified
508 :     boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 0;
509 :     if (keepHistory) {
510 :     tree.addToLocalHistory(source);
511 :     }
512 :    
513 :     // try to move the source file in the local file system
514 :     java.io.File lfsSource = source.getLocation().toFile();
515 :     java.io.File lfsDestination = destination.getLocation().toFile();
516 :     boolean moveSuccess = moveLocalFile(lfsSource, lfsDestination);
517 :     if (moveSuccess) {
518 :     // update the workspace resource tree to match
519 :     tree.movedFile(source, destination);
520 :     // moveLocalFile may have affected timestamp
521 :     // update timestamp to avoid having out of sync destination
522 :     tree.updateMovedFileTimestamp(destination, tree.computeTimestamp(destination));
523 :     // return true to say that the operation has been done
524 :     return true;
525 :     } else {
526 :     // report an unexpected failure
527 :     Status status =
528 :     new Status(
529 :     Status.ERROR,
530 :     "com.example.movedeletehook",
531 :     0,
532 :     "Unable to move file " + source.getFullPath() + " in the local file system",
533 :     null);
534 :     tree.failed(status);
535 :     // return true to say that the operation has been done
536 :     return true;
537 :     }
538 :     }
539 :    
540 :     /**
541 :     * This <code>IMoveDeleteHook</code> method implements
542 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code> where the receiver
543 :     * is a folder. This example implementation illustrates the steps involved
544 :     * (except for progress monitoring). The general approach illustrated here
545 :     * moves the entire subtree in the local file system and then fixes
546 :     * up the workspace resource tree to match using
547 :     * <code>IResourceTree.movedFolder</code>.
548 :     *
549 :     * @param tree the workspace resource tree; this object is only valid
550 :     * for the duration of the invocation of this method, and must not be
551 :     * used after this method has completed
552 :     * @param source the handle of the folder to move; the receiver of
553 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>;
554 :     * guaranteed to exist in the workspace resource tree
555 :     * @param destination the handle of where the folder will move to; the
556 :     * handle equivalent of the first parameter to
557 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>
558 :     * guaranteed to not exist in the workspace resource tree;
559 :     * parent container guaranteed to exist and be open
560 :     * @param updateFlags bit-wise or of update flag constants as per
561 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>
562 :     * @param monitor the progress monitor, or <code>null</code> as per
563 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>
564 :     * @return <code>false</code> if this method declined to assume
565 :     * responsibility for this operation, and <code>true</code> if this
566 :     * method attempted to carry out the operation
567 :     * @see IResource#move(IPath,int,IProgressMonitor)
568 :     * @see IMoveDeleteHook#moveFolder(IResourceTree,IFolder,IFolder,int,IProgressMonitor)
569 :     */
570 :     public boolean moveFolder(
571 :     IResourceTree tree,
572 :     IFolder source,
573 :     IFolder destination,
574 :     int updateFlags,
575 :     IProgressMonitor monitor) {
576 :    
577 :     // Given: source exists in workspace resource tree
578 :     // Given: destination does not exist in workspace resource tree
579 :     // Given: destination parent exists and is open in workspace resource tree
580 :     if (!source.exists()
581 :     || destination.exists()
582 :     || !destination.getParent().isAccessible()) {
583 :     throw new IllegalArgumentException();
584 :     }
585 :    
586 :     // if FORCE is not specified, fail if the workspace resource tree is
587 :     // not in sync at source folder and its descendents in the local file system
588 :     boolean force = (updateFlags & IResource.FORCE) != 0;
589 :     if (!force) {
590 :     boolean inSync = tree.isSynchronized(source, IResource.DEPTH_INFINITE);
591 :     if (!inSync) {
592 :     // report failure
593 :     Status status =
594 :     new Status(
595 :     Status.ERROR,
596 :     "com.example.movedeletehook",
597 :     0,
598 :     "Folder " + source.getFullPath() + " is out of sync with the local file system",
599 :     null);
600 :     tree.failed(status);
601 :     // return true to say that the operation has been done
602 :     return true;
603 :     }
604 :     }
605 :    
606 :     // capture the current state of all files in the local history if
607 :     // KEEP_HISTORY is specified
608 :     boolean keepHistory = (updateFlags & IResource.KEEP_HISTORY) != 0;
609 :     if (keepHistory) {
610 :     addAllFilesToHistory(tree, source);
611 :     }
612 :    
613 :     // try to move the subtree in the local file system
614 :     java.io.File lfsSource = source.getLocation().toFile();
615 :     java.io.File lfsDestination = destination.getLocation().toFile();
616 :     boolean lfsSuccess = moveLocalSubtree(lfsSource, lfsDestination);
617 :     if (lfsSuccess) {
618 :     // update the workspace resource tree to match
619 :     tree.movedFolderSubtree(source, destination);
620 :     // moveLocalSubtree may have affected file timestamps
621 :     // update file timestamps to avoid having out of sync destination
622 :     updateTimestamps(tree, destination);
623 :     // return true to say that the operation has been done
624 :     return true;
625 :     } else {
626 :     // report an unexpected failure
627 :     Status status =
628 :     new Status(
629 :     Status.ERROR,
630 :     "com.example.movedeletehook",
631 :     0,
632 :     "Unable to move folder " + source.getFullPath() + " in the local file system",
633 :     null);
634 :     tree.failed(status);
635 :     // return true to say that the operation has been done
636 :     return true;
637 :     }
638 :     }
639 :    
640 :     /**
641 :     * Updates timestamps for all files in the workspace resource subtree rooted
642 :     * at the given container. The given container must exist and be accessible.
643 :     * <p>
644 :     * Note that this is <b>not</b> the same thing as <code>refreshLocal</code>,
645 :     * because (a) it only updates file timestamps for file known to the
646 :     * workspace resource tree, and (b) the updated timestamps are not
647 :     * considered to be changes.
648 :     *
649 :     * @param tree the workspace resource tree
650 :     * @param container the root container
651 :     */
652 :     private void updateTimestamps(final IResourceTree tree, IContainer container) {
653 :    
654 :     // Resource visitor for updtating timestamp for files it visits
655 :     class UpdateVisitor implements IResourceVisitor {
656 :     public boolean visit(IResource resource) {
657 :     if (resource.getType() == IResource.FILE) {
658 :     IFile file = (IFile) resource;
659 :     tree.updateMovedFileTimestamp(file, tree.computeTimestamp(file));
660 :     }
661 :     return true;
662 :     }
663 :     }
664 :    
665 :     try {
666 :     // update timestamps for both regular and team-private members
667 :     container.accept(
668 :     new UpdateVisitor(),
669 :     IResource.DEPTH_INFINITE,
670 :     IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
671 :     } catch (CoreException e) {
672 :     // visitor does not throw CoreException
673 :     // container is known to be accessible
674 :     // exception should not happen
675 :     throw new RuntimeException();
676 :     }
677 :     }
678 :    
679 :     /**
680 :     * This <code>IMoveDeleteHook</code> method implements
681 :     * <code>IResource.move(IProjectDescrition,int,IProgressMonitor)</code>
682 :     * where the receiver is a project. This example implementation illustrates
683 :     * the steps involved (except for progress monitoring). The general approach
684 :     * illustrated here relocates the entire project content are in the local
685 :     * file system and then fixes up the workspace resource tree to match using
686 :     * <code>IResourceTree.movedProject</code>.
687 :     *
688 :     * @param tree the workspace resource tree; this object is only valid
689 :     * for the duration of the invocation of this method, and must not be
690 :     * used after this method has completed
691 :     * @param source the handle of the project to move; the receiver of
692 :     * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>
693 :     * or <code>IResource.move(IPath,int,IProgressMonitor)</code>;
694 :     * guaranteed to exist and be open in the workspace resource tree
695 :     * @param description the new description of the project; the first
696 :     * parameter to
697 :     * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>, or
698 :     * a copy of the project's description with the location changed to the
699 :     * path given in the first parameter to
700 :     * <code>IResource.move(IPath,int,IProgressMonitor)</code>
701 :     * @param updateFlags bit-wise or of update flag constants as per
702 :     * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>
703 :     * or <code>IResource.move(IPath,int,IProgressMonitor)</code>
704 :     * @param monitor the progress monitor, or <code>null</code> as per
705 :     * <code>IResource.move(IProjectDescription,int,IProgressMonitor)</code>
706 :     * or <code>IResource.move(IPath,int,IProgressMonitor)</code>
707 :     * @return <code>false</code> if this method declined to assume
708 :     * responsibility for this operation, and <code>true</code> if this method
709 :     * attempted to carry out the operation
710 :     * @see IResource#move(IPath,int,IProgressMonitor)
711 :     * @see IResource#move(IProjectDescription,int,IProgressMonitor)
712 :     * @see IMoveDeleteHook#moveProject(IResourceTree,IProject,IProjectDescription,int,IProgressMonitor)
713 :     */
714 :     public boolean moveProject(
715 :     IResourceTree tree,
716 :     IProject project,
717 :     IProjectDescription description,
718 :     int updateFlags,
719 :     IProgressMonitor monitor) {
720 :    
721 :     // Given: source exists and is open in workspace resource tree
722 :     if (!project.exists() || !project.isOpen()) {
723 :     throw new IllegalArgumentException();
724 :     }
725 :    
726 :     // are we changing the name of the project?
727 :     // this affects the workspace resource tree
728 :     String oldProjectName = project.getName();
729 :     String newProjectName = description.getName();
730 :     boolean renaming = !newProjectName.equals(oldProjectName);
731 :    
732 :     // are we changing the location of the project content area?
733 :     // this affects the files in the local file system
734 :     // and the workspace resource tree
735 :     IPath oldProjectContentArea = project.getLocation();
736 :     IPath newProjectContentArea = description.getLocation();
737 :     if (newProjectContentArea == null) {
738 :     // compute path of new default project content area
739 :     newProjectContentArea = Platform.getLocation().append(newProjectName);
740 :     }
741 :     boolean relocating = !newProjectContentArea.equals(oldProjectContentArea);
742 :    
743 :     IProject newProject =
744 :     project.getWorkspace().getRoot().getProject(newProjectName);
745 :    
746 :     if (!renaming && !relocating) {
747 :     // the hook method should never have been called
748 :     throw new IllegalArgumentException();
749 :     }
750 :    
751 :     // fail we are renaming to project that exists in workspace resource tree
752 :     if (renaming && newProject.exists()) {
753 :     // report failure
754 :     Status status =
755 :     new Status(
756 :     Status.ERROR,
757 :     "com.example.movedeletehook",
758 :     0,
759 :     "Project " + newProject.getFullPath() + " already exists",
760 :     null);
761 :     tree.failed(status);
762 :     // return true to say that the operation has been done
763 :     return true;
764 :     }
765 :    
766 :     // relocate the project content area
767 :     if (relocating) {
768 :     // need to move files in local file system
769 :     boolean lfsMoveSuccess =
770 :     moveLocalSubtree(
771 :     oldProjectContentArea.toFile(),
772 :     newProjectContentArea.toFile());
773 :     if (!lfsMoveSuccess) {
774 :     // report failure
775 :     Status status =
776 :     new Status(
777 :     Status.ERROR,
778 :     "com.example.movedeletehook",
779 :     0,
780 :     "Unable to move project contents for " + newProject.getFullPath(),
781 :     null);
782 :     tree.failed(status);
783 :     // return true to say that the operation has been done
784 :     return true;
785 :     }
786 :     }
787 :    
788 :     // update project in workspace resource tree
789 :     boolean treeMoveSuccess = tree.movedProjectSubtree(project, description);
790 :     if (!treeMoveSuccess) {
791 :     // report failure
792 :     Status status =
793 :     new Status(
794 :     Status.ERROR,
795 :     "com.example.movedeletehook",
796 :     0,
797 :     "Unable to move project " + project.getFullPath(),
798 :     null);
799 :     tree.failed(status);
800 :     // return true to say that the operation has been done
801 :     return true;
802 :     }
803 :    
804 :     if (relocating) {
805 :     // moveLocalSubtree may have affected file timestamps
806 :     // update file timestamps to avoid having out of sync destination
807 :     updateTimestamps(tree, newProject);
808 :     }
809 :     // return true to say that the operation has been done
810 :     return true;
811 :     }
812 :    
813 :     /**
814 :     * Captures local history for all files in the subtree rooted at the
815 :     * given container. The given container must exist and be accessible.
816 :     *
817 :     * @param tree the workspace resource tree
818 :     * @param container the root container
819 :     */
820 :     private void addAllFilesToHistory(
821 :     final IResourceTree tree,
822 :     IContainer container) {
823 :    
824 :     // Resource visitor for keeping local listory for files it visits
825 :     class KeepVisitor implements IResourceVisitor {
826 :     public boolean visit(IResource resource) {
827 :     if (resource.getType() == IResource.FILE) {
828 :     // capture current state of file in local history
829 :     tree.addToLocalHistory((IFile) resource);
830 :     }
831 :     return true;
832 :     }
833 :     }
834 :    
835 :     try {
836 :     // only save history for regular members as there is little point
837 :     // in saving history for team-private members
838 :     container.accept(new KeepVisitor(), IResource.DEPTH_INFINITE, IResource.NONE);
839 :     } catch (CoreException e) {
840 :     // visitor does not throw CoreException
841 :     // container is known to be accessible
842 :     // exception should not happen
843 :     throw new RuntimeException();
844 :     }
845 :     }
846 :    
847 :     /**
848 :     * Deletes the given directory subtree in the local file system.
849 :     * Returns <code>true</code> immediately if the given folder does
850 :     * not exist.
851 :     *
852 :     * @param lfsFolder the folder in the local file system
853 :     * @return <code>true</code> if the given folder no longer exists,
854 :     * and <code>false</code> if it was not deleted
855 :     */
856 :     private boolean deleteLocalSubtree(java.io.File lfsFolder) {
857 :     java.io.File[] lfsChildren = lfsFolder.listFiles();
858 :     if (lfsChildren == null) {
859 :     if (lfsFolder.exists()) {
860 :     // folder does exists (I/O error or not a directory)
861 :     return false;
862 :     } else {
863 :     // folder does not exist
864 :     return true;
865 :     }
866 :     }
867 :     // delete all children first
868 :     for (int i = 0; i < lfsChildren.length; i++) {
869 :     java.io.File lfsChild = lfsChildren[i];
870 :     if (lfsChild.isFile()) {
871 :     // attempt to delete the file
872 :     boolean childSuccess = lfsChild.delete();
873 :     // don't worry if we could not delete the child file
874 :     // we will be unable to delete the parent folder if it still
875 :     // has children
876 :     } else if (lfsChild.isDirectory()) {
877 :     // otherwise recurse over the child subtree
878 :     boolean childSuccess = deleteLocalSubtree(lfsChild);
879 :     // don't worry if we could not delete the child folder
880 :     // we will be unable to delete the parent folder if it still
881 :     // has children
882 :     }
883 :     }
884 :     // delete folder now that children should be gone
885 :     boolean success = lfsFolder.delete();
886 :     // we're happy as long as folder is gone
887 :     return !lfsFolder.exists();
888 :     }
889 :    
890 :     /**
891 :     * Copies the given source file in the local file system to the given
892 :     * destination file. The operation fails rather than overwriting an
893 :     * existing destination file. The destination parent folder will be
894 :     * created if required.
895 :     *
896 :     * @param lfsSource the source file in the local file system; this is
897 :     * the file to be copied
898 :     * @param lfsDestination the destination file in the local file system;
899 :     * this is where the file is to be copied to
900 :     * @return <code>true</code> if the copy was successful,
901 :     * and <code>false</code> if the copy failed
902 :     */
903 :     private boolean copyLocalFile(
904 :     java.io.File lfsSource,
905 :     java.io.File lfsDestination) {
906 :    
907 :     // create the destination parent folder if required
908 :     java.io.File lfsDestinationParent = lfsDestination.getParentFile();
909 :     if (lfsDestinationParent != null && !lfsDestinationParent.exists()) {
910 :     boolean mkdirSuccess = lfsDestinationParent.mkdirs();
911 :     if (!mkdirSuccess) {
912 :     // fail if unable to create destination parent folder
913 :     return false;
914 :     }
915 :     }
916 :    
917 :     if (lfsDestination.exists()) {
918 :     // so that we do not overwrite an existing file
919 :     return false;
920 :     }
921 :    
922 :     InputStream in = null;
923 :     OutputStream out = null;
924 :     try {
925 :     in = new BufferedInputStream(new FileInputStream(lfsSource));
926 :     out = new BufferedOutputStream(new FileOutputStream(lfsDestination));
927 :     long fileSize = lfsSource.length();
928 :     // use 10KB buffer for small files
929 :     int bufferSize = 10 * 1024;
930 :     if (fileSize > 100 * 1024) {
931 :     // use 100KB buffer for medium-sized files
932 :     bufferSize = 100 * 1024;
933 :     }
934 :     if (fileSize > 1000 * 1024) {
935 :     // use 1MB buffer for large files
936 :     bufferSize = 1000 * 1024;
937 :     }
938 :     byte[] buffer = new byte[bufferSize];
939 :     while (true) {
940 :     int bytesRead = in.read(buffer);
941 :     if (bytesRead < 0) {
942 :     break;
943 :     }
944 :     out.write(buffer, 0, bytesRead);
945 :     }
946 :     return true;
947 :     } catch (FileNotFoundException e) {
948 :     // unable to open the source file for input
949 :     // or unable to open the destination file for output
950 :     // fail in either case
951 :     return false;
952 :     } catch (IOException e) {
953 :     // fail
954 :     return false;
955 :     } finally {
956 :     // close both streams in all cases
957 :     try {
958 :     if (in != null) {
959 :     in.close();
960 :     }
961 :     } catch (IOException closeException) {
962 :     // ignore
963 :     }
964 :     try {
965 :     if (out != null) {
966 :     out.close();
967 :     }
968 :     } catch (IOException closeException) {
969 :     // better safe than sorry
970 :     // fail if we have problems closing the output stream
971 :     return false;
972 :     }
973 :     }
974 :     }
975 :    
976 :     /**
977 :     * Copies the given folder subtree in the local file system to the given
978 :     * destination folder. The operation fails rather than overwriting an
979 :     * existing file. The destination parent folder will be created if required.
980 :     *
981 :     * @param lfsSource the source folder in the local file system; this is
982 :     * the folder to be copied
983 :     * @param lfsDestination the destination folder in the local file system;
984 :     * this is where the folder is to be copied to
985 :     * @return <code>true</code> if the copy was successful,
986 :     * and <code>false</code> if the copy failed
987 :     */
988 :     private boolean copyLocalSubtree(
989 :     java.io.File lfsSource,
990 :     java.io.File lfsDestination) {
991 :    
992 :     java.io.File[] lfsChildren = lfsSource.listFiles();
993 :     if (lfsChildren == null) {
994 :     // folder does not exist, I/O error, or not a directory
995 :     // fail since no source to copy
996 :     return false;
997 :     }
998 :    
999 :     // create the destination folder if required
1000 :     if (!lfsDestination.exists()) {
1001 :     boolean mkdirSuccess = lfsDestination.mkdirs();
1002 :     if (!mkdirSuccess) {
1003 :     // fail if unable to create destination folder
1004 :     return false;
1005 :     }
1006 :     }
1007 :    
1008 :     // copy all children
1009 :     for (int i = 0; i < lfsChildren.length; i++) {
1010 :     java.io.File lfsChild = lfsChildren[i];
1011 :     java.io.File lfsDestinationChild =
1012 :     new java.io.File(lfsDestination, lfsChild.getName());
1013 :     boolean childSuccess = false;
1014 :     if (lfsChild.isFile()) {
1015 :     // attempt to copy the file
1016 :     childSuccess = copyLocalFile(lfsChild, lfsDestinationChild);
1017 :     } else if (lfsChild.isDirectory()) {
1018 :     // otherwise recursively copy the child folder
1019 :     childSuccess = copyLocalSubtree(lfsChild, lfsDestinationChild);
1020 :     }
1021 :     if (!childSuccess) {
1022 :     // fail immediately if unable to successfully copy a child
1023 :     return false;
1024 :     }
1025 :     }
1026 :     // succeed since all children were successfully copied
1027 :     return true;
1028 :     }
1029 :    
1030 :     /**
1031 :     * Moves the given source file in the local file system to the given
1032 :     * destination file. The destination's parent folder will be created
1033 :     * if required. The timestamp of the file may change in the process.
1034 :     *
1035 :     * @param lfsSource the source file in the local file system; this is
1036 :     * the file to be moved
1037 :     * @param lfsDestination the destination file in the local file system;
1038 :     * this is where the file is to be moved to
1039 :     * @return <code>true</code> if the move was successful,
1040 :     * and <code>false</code> if the move failed
1041 :     */
1042 :     private boolean moveLocalFile(
1043 :     java.io.File lfsSource,
1044 :     java.io.File lfsDestination) {
1045 :    
1046 :     // create the destination parent (and ancestors) if required
1047 :     java.io.File lfsDestinationParent = lfsDestination.getParentFile();
1048 :     if (lfsDestinationParent != null && !lfsDestinationParent.exists()) {
1049 :     boolean mkdirsSuccess = lfsDestinationParent.mkdirs();
1050 :     if (!mkdirsSuccess) {
1051 :     // fail because destination parent cannot be created
1052 :     return false;
1053 :     }
1054 :     }
1055 :    
1056 :     // attempt to rename the file
1057 :     boolean renameSuccess = lfsSource.renameTo(lfsDestination);
1058 :     if (renameSuccess) {
1059 :     // that was easy
1060 :     return true;
1061 :     }
1062 :    
1063 :     // plan B: copy the file and delete the original
1064 :     boolean copySuccess = copyLocalFile(lfsSource, lfsDestination);
1065 :     if (!copySuccess) {
1066 :     // fail if unable to make copy
1067 :     return false;
1068 :     }
1069 :    
1070 :     // delete the source file
1071 :     boolean deleteSuccess = lfsSource.delete();
1072 :     // operation succeeds iff we copied source and successfully deleted it
1073 :     return deleteSuccess;
1074 :     }
1075 :    
1076 :     /**
1077 :     * Moves the given subtree in the local file system to the given
1078 :     * destination. The destination's parent folder will be created
1079 :     * if required.
1080 :     *
1081 :     * @param lfsSource the source folder in the local file system; this is
1082 :     * the folder to be moved
1083 :     * @param lfsDestination the destination folder in the local file system;
1084 :     * this is where the folder is to be moved to
1085 :     * @return <code>true</code> if the move was successful,
1086 :     * and <code>false</code> if the move failed
1087 :     */
1088 :     private boolean moveLocalSubtree(
1089 :     java.io.File lfsSource,
1090 :     java.io.File lfsDestination) {
1091 :    
1092 :     // create the destination parent (and ancestors) if required
1093 :     java.io.File lfsDestinationParent = lfsDestination.getParentFile();
1094 :     if (lfsDestinationParent != null && !lfsDestinationParent.exists()) {
1095 :     boolean mkdirsSuccess = lfsDestinationParent.mkdirs();
1096 :     if (!mkdirsSuccess) {
1097 :     // fail because destination parent cannot be created
1098 :     return false;
1099 :     }
1100 :     }
1101 :    
1102 :     // attempt to rename the folder
1103 :     boolean renameSuccess = lfsSource.renameTo(lfsDestination);
1104 :     if (renameSuccess) {
1105 :     // that was easy
1106 :     return true;
1107 :     }
1108 :    
1109 :     // plan B: copy the folder and then delete the original
1110 :     boolean copySuccess = copyLocalSubtree(lfsSource, lfsDestination);
1111 :     if (!copySuccess) {
1112 :     // fail if unable to make copy
1113 :     return false;
1114 :     }
1115 :    
1116 :     // delete the source folder
1117 :     boolean deleteSuccess = deleteLocalSubtree(lfsSource);
1118 :     // operation succeeds iff we copied source and successfully deleted it
1119 :     return deleteSuccess;
1120 :     }
1121 :     }