001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.wizards.datatransfer;
011:
012: import java.io.File;
013: import java.io.IOException;
014: import java.util.ArrayList;
015: import java.util.Iterator;
016: import java.util.List;
017:
018: import org.eclipse.core.resources.IContainer;
019: import org.eclipse.core.resources.IFile;
020: import org.eclipse.core.resources.IResource;
021: import org.eclipse.core.runtime.CoreException;
022: import org.eclipse.core.runtime.IPath;
023: import org.eclipse.core.runtime.IProgressMonitor;
024: import org.eclipse.core.runtime.IStatus;
025: import org.eclipse.core.runtime.MultiStatus;
026: import org.eclipse.core.runtime.Path;
027: import org.eclipse.core.runtime.Status;
028: import org.eclipse.jface.operation.IRunnableWithProgress;
029: import org.eclipse.jface.operation.ModalContext;
030: import org.eclipse.osgi.util.NLS;
031: import org.eclipse.ui.PlatformUI;
032: import org.eclipse.ui.dialogs.IOverwriteQuery;
033:
034: /**
035: * Operation for exporting the contents of a resource to the local file system.
036: */
037: public class FileSystemExportOperation implements IRunnableWithProgress {
038: private IPath path;
039:
040: private IProgressMonitor monitor;
041:
042: private FileSystemExporter exporter = new FileSystemExporter();
043:
044: private List resourcesToExport;
045:
046: private IOverwriteQuery overwriteCallback;
047:
048: private IResource resource;
049:
050: private List errorTable = new ArrayList(1);
051:
052: //The constants for the overwrite 3 state
053: private static final int OVERWRITE_NOT_SET = 0;
054:
055: private static final int OVERWRITE_NONE = 1;
056:
057: private static final int OVERWRITE_ALL = 2;
058:
059: private int overwriteState = OVERWRITE_NOT_SET;
060:
061: private boolean createLeadupStructure = true;
062:
063: private boolean createContainerDirectories = true;
064:
065: /**
066: * Create an instance of this class. Use this constructor if you wish to
067: * recursively export a single resource
068: */
069: public FileSystemExportOperation(IResource res,
070: String destinationPath, IOverwriteQuery overwriteImplementor) {
071: super ();
072: resource = res;
073: path = new Path(destinationPath);
074: overwriteCallback = overwriteImplementor;
075: }
076:
077: /**
078: * Create an instance of this class. Use this constructor if you wish to
079: * export specific resources with a common parent resource (affects container
080: * directory creation)
081: */
082: public FileSystemExportOperation(IResource res, List resources,
083: String destinationPath, IOverwriteQuery overwriteImplementor) {
084: this (res, destinationPath, overwriteImplementor);
085: resourcesToExport = resources;
086: }
087:
088: /**
089: * Answer the total number of file resources that exist at or below self in the
090: * resources hierarchy.
091: *
092: * @return int
093: * @param parentResource org.eclipse.core.resources.IResource
094: */
095: protected int countChildrenOf(IResource parentResource)
096: throws CoreException {
097: if (parentResource.getType() == IResource.FILE) {
098: return 1;
099: }
100:
101: int count = 0;
102: if (parentResource.isAccessible()) {
103: IResource[] children = ((IContainer) parentResource)
104: .members();
105: for (int i = 0; i < children.length; i++) {
106: count += countChildrenOf(children[i]);
107: }
108: }
109:
110: return count;
111: }
112:
113: /**
114: * Answer a boolean indicating the number of file resources that were
115: * specified for export
116: *
117: * @return int
118: */
119: protected int countSelectedResources() throws CoreException {
120: int result = 0;
121: Iterator resources = resourcesToExport.iterator();
122:
123: while (resources.hasNext()) {
124: result += countChildrenOf((IResource) resources.next());
125: }
126:
127: return result;
128: }
129:
130: /**
131: * Create the directories required for exporting the passed resource,
132: * based upon its container hierarchy
133: *
134: * @param childResource org.eclipse.core.resources.IResource
135: */
136: protected void createLeadupDirectoriesFor(IResource childResource) {
137: IPath resourcePath = childResource.getFullPath()
138: .removeLastSegments(1);
139:
140: for (int i = 0; i < resourcePath.segmentCount(); i++) {
141: path = path.append(resourcePath.segment(i));
142: exporter.createFolder(path);
143: }
144: }
145:
146: /**
147: * Recursively export the previously-specified resource
148: */
149: protected void exportAllResources() throws InterruptedException {
150: if (resource.getType() == IResource.FILE) {
151: exportFile((IFile) resource, path);
152: } else {
153: try {
154: exportChildren(((IContainer) resource).members(), path);
155: } catch (CoreException e) {
156: // not safe to show a dialog
157: // should never happen because the file system export wizard ensures that the
158: // single resource chosen for export is both existent and accessible
159: errorTable.add(e.getStatus());
160: }
161: }
162: }
163:
164: /**
165: * Export all of the resources contained in the passed collection
166: *
167: * @param children java.util.Enumeration
168: * @param currentPath IPath
169: */
170: protected void exportChildren(IResource[] children,
171: IPath currentPath) throws InterruptedException {
172: for (int i = 0; i < children.length; i++) {
173: IResource child = children[i];
174: if (!child.isAccessible()) {
175: continue;
176: }
177:
178: if (child.getType() == IResource.FILE) {
179: exportFile((IFile) child, currentPath);
180: } else {
181: IPath destination = currentPath.append(child.getName());
182: exporter.createFolder(destination);
183: try {
184: exportChildren(((IContainer) child).members(),
185: destination);
186: } catch (CoreException e) {
187: // not safe to show a dialog
188: // should never happen because:
189: // i. this method is called recursively iterating over the result of #members,
190: // which only answers existing children
191: // ii. there is an #isAccessible check done before #members is invoked
192: errorTable.add(e.getStatus());
193: }
194: }
195: }
196: }
197:
198: /**
199: * Export the passed file to the specified location
200: *
201: * @param file org.eclipse.core.resources.IFile
202: * @param location org.eclipse.core.runtime.IPath
203: */
204: protected void exportFile(IFile file, IPath location)
205: throws InterruptedException {
206: IPath fullPath = location.append(file.getName());
207: monitor.subTask(file.getFullPath().toString());
208: String properPathString = fullPath.toOSString();
209: File targetFile = new File(properPathString);
210:
211: if (targetFile.exists()) {
212: if (!targetFile.canWrite()) {
213: errorTable
214: .add(new Status(
215: IStatus.ERROR,
216: PlatformUI.PLUGIN_ID,
217: 0,
218: NLS
219: .bind(
220: DataTransferMessages.DataTransfer_cannotOverwrite,
221: targetFile
222: .getAbsolutePath()),
223: null));
224: monitor.worked(1);
225: return;
226: }
227:
228: if (overwriteState == OVERWRITE_NONE) {
229: return;
230: }
231:
232: if (overwriteState != OVERWRITE_ALL) {
233: String overwriteAnswer = overwriteCallback
234: .queryOverwrite(properPathString);
235:
236: if (overwriteAnswer.equals(IOverwriteQuery.CANCEL)) {
237: throw new InterruptedException();
238: }
239:
240: if (overwriteAnswer.equals(IOverwriteQuery.NO)) {
241: monitor.worked(1);
242: return;
243: }
244:
245: if (overwriteAnswer.equals(IOverwriteQuery.NO_ALL)) {
246: monitor.worked(1);
247: overwriteState = OVERWRITE_NONE;
248: return;
249: }
250:
251: if (overwriteAnswer.equals(IOverwriteQuery.ALL)) {
252: overwriteState = OVERWRITE_ALL;
253: }
254: }
255: }
256:
257: try {
258: exporter.write(file, fullPath);
259: } catch (IOException e) {
260: errorTable
261: .add(new Status(
262: IStatus.ERROR,
263: PlatformUI.PLUGIN_ID,
264: 0,
265: NLS
266: .bind(
267: DataTransferMessages.DataTransfer_errorExporting,
268: fullPath, e.getMessage()),
269: e));
270: } catch (CoreException e) {
271: errorTable
272: .add(new Status(
273: IStatus.ERROR,
274: PlatformUI.PLUGIN_ID,
275: 0,
276: NLS
277: .bind(
278: DataTransferMessages.DataTransfer_errorExporting,
279: fullPath, e.getMessage()),
280: e));
281: }
282:
283: monitor.worked(1);
284: ModalContext.checkCanceled(monitor);
285: }
286:
287: /**
288: * Export the resources contained in the previously-defined
289: * resourcesToExport collection
290: */
291: protected void exportSpecifiedResources()
292: throws InterruptedException {
293: Iterator resources = resourcesToExport.iterator();
294: IPath initPath = (IPath) path.clone();
295:
296: while (resources.hasNext()) {
297: IResource currentResource = (IResource) resources.next();
298: if (!currentResource.isAccessible()) {
299: continue;
300: }
301:
302: path = initPath;
303:
304: if (resource == null) {
305: // No root resource specified and creation of containment directories
306: // is required. Create containers from depth 2 onwards (ie.- project's
307: // child inclusive) for each resource being exported.
308: if (createLeadupStructure) {
309: createLeadupDirectoriesFor(currentResource);
310: }
311:
312: } else {
313: // Root resource specified. Must create containment directories
314: // from this point onwards for each resource being exported
315: IPath containersToCreate = currentResource
316: .getFullPath().removeFirstSegments(
317: resource.getFullPath().segmentCount())
318: .removeLastSegments(1);
319:
320: for (int i = 0; i < containersToCreate.segmentCount(); i++) {
321: path = path.append(containersToCreate.segment(i));
322: exporter.createFolder(path);
323: }
324: }
325:
326: if (currentResource.getType() == IResource.FILE) {
327: exportFile((IFile) currentResource, path);
328: } else {
329: if (createContainerDirectories) {
330: path = path.append(currentResource.getName());
331: exporter.createFolder(path);
332: }
333:
334: try {
335: exportChildren(((IContainer) currentResource)
336: .members(), path);
337: } catch (CoreException e) {
338: // should never happen because #isAccessible is called before #members is invoked,
339: // which implicitly does an existence check
340: errorTable.add(e.getStatus());
341: }
342: }
343: }
344: }
345:
346: /**
347: * Returns the status of the export operation.
348: * If there were any errors, the result is a status object containing
349: * individual status objects for each error.
350: * If there were no errors, the result is a status object with error code <code>OK</code>.
351: *
352: * @return the status
353: */
354: public IStatus getStatus() {
355: IStatus[] errors = new IStatus[errorTable.size()];
356: errorTable.toArray(errors);
357: return new MultiStatus(
358: PlatformUI.PLUGIN_ID,
359: IStatus.OK,
360: errors,
361: DataTransferMessages.FileSystemExportOperation_problemsExporting,
362: null);
363: }
364:
365: /**
366: * Answer a boolean indicating whether the passed child is a descendent
367: * of one or more members of the passed resources collection
368: *
369: * @return boolean
370: * @param resources java.util.List
371: * @param child org.eclipse.core.resources.IResource
372: */
373: protected boolean isDescendent(List resources, IResource child) {
374: if (child.getType() == IResource.PROJECT) {
375: return false;
376: }
377:
378: IResource parent = child.getParent();
379: if (resources.contains(parent)) {
380: return true;
381: }
382:
383: return isDescendent(resources, parent);
384: }
385:
386: /**
387: * Export the resources that were previously specified for export
388: * (or if a single resource was specified then export it recursively)
389: */
390: public void run(IProgressMonitor progressMonitor)
391: throws InterruptedException {
392: this .monitor = progressMonitor;
393:
394: if (resource != null) {
395: if (createLeadupStructure) {
396: createLeadupDirectoriesFor(resource);
397: }
398:
399: if (createContainerDirectories
400: && resource.getType() != IResource.FILE) {
401: // ensure it's a container
402: path = path.append(resource.getName());
403: exporter.createFolder(path);
404: }
405: }
406:
407: try {
408: int totalWork = IProgressMonitor.UNKNOWN;
409: try {
410: if (resourcesToExport == null) {
411: totalWork = countChildrenOf(resource);
412: } else {
413: totalWork = countSelectedResources();
414: }
415: } catch (CoreException e) {
416: // Should not happen
417: errorTable.add(e.getStatus());
418: }
419: monitor.beginTask(
420: DataTransferMessages.DataTransfer_exportingTitle,
421: totalWork);
422: if (resourcesToExport == null) {
423: exportAllResources();
424: } else {
425: exportSpecifiedResources();
426: }
427: } finally {
428: monitor.done();
429: }
430: }
431:
432: /**
433: * Set this boolean indicating whether a directory should be created for
434: * Folder resources that are explicitly passed for export
435: *
436: * @param value boolean
437: */
438: public void setCreateContainerDirectories(boolean value) {
439: createContainerDirectories = value;
440: }
441:
442: /**
443: * Set this boolean indicating whether each exported resource's complete path should
444: * include containment hierarchies as dictated by its parents
445: *
446: * @param value boolean
447: */
448: public void setCreateLeadupStructure(boolean value) {
449: createLeadupStructure = value;
450: }
451:
452: /**
453: * Set this boolean indicating whether exported resources should automatically
454: * overwrite existing files when a conflict occurs. If not
455: * query the user.
456: *
457: * @param value boolean
458: */
459: public void setOverwriteFiles(boolean value) {
460: if (value) {
461: overwriteState = OVERWRITE_ALL;
462: }
463: }
464: }
|