Changes:
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / obfuscator / export / ObfuscatorExportOperation.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpeclipse.obfuscator.export;
12  
13 import java.io.File;
14 import java.io.IOException;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
19
20 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
21 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
22 import net.sourceforge.phpeclipse.obfuscator.ObfuscatorIgnores;
23 import net.sourceforge.phpeclipse.obfuscator.ObfuscatorPass1Exporter;
24 import net.sourceforge.phpeclipse.obfuscator.ObfuscatorPass2Exporter;
25
26 import org.eclipse.core.resources.IContainer;
27 import org.eclipse.core.resources.IFile;
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IPath;
32 import org.eclipse.core.runtime.IProgressMonitor;
33 import org.eclipse.core.runtime.IStatus;
34 import org.eclipse.core.runtime.MultiStatus;
35 import org.eclipse.core.runtime.Path;
36 import org.eclipse.core.runtime.Status;
37 import org.eclipse.jface.operation.IRunnableWithProgress;
38 import org.eclipse.jface.operation.ModalContext;
39 import org.eclipse.jface.preference.IPreferenceStore;
40 import org.eclipse.ui.PlatformUI;
41 import org.eclipse.ui.dialogs.IOverwriteQuery;
42  
43
44 /**
45  *      Operation for exporting the contents of a resource to the local file system.
46  */
47 /*package*/
48 class ObfuscatorExportOperation implements IRunnableWithProgress {
49   private IPath fPath;
50   private IProgressMonitor fMonitor;
51   private ObfuscatorPass1Exporter fExporter1 = null;
52   private ObfuscatorPass2Exporter fExporter2 = null;
53   private HashMap fCurrentIdentifierMap = null;
54   private HashMap fProjectMap = null;
55   private String fCurrentProjectName = "";
56
57   private List fResourcesToExport;
58   private IOverwriteQuery fOverwriteCallback;
59   private IResource fResource;
60   private List errorTable = new ArrayList(1);
61
62   //The constants for the overwrite 3 state
63   private static final int OVERWRITE_NOT_SET = 0;
64   private static final int OVERWRITE_NONE = 1;
65   private static final int OVERWRITE_ALL = 2;
66   private int overwriteState = OVERWRITE_NOT_SET;
67
68   //  private boolean createLeadupStructure = true;
69   private boolean createContainerDirectories = true;
70   /**
71    *  Create an instance of this class.  Use this constructor if you wish to
72    *  export specific resources without a common parent resource
73    */
74   //  public ObfuscatorExportOperation(List resources, String destinationPath, IOverwriteQuery overwriteImplementor) {
75   //    super();
76   //
77   //    exporter1 = new ObfuscatorPass1Exporter(new Scanner(false, false), identifierMap);
78   //            exporter2 = new ObfuscatorPass2Exporter(new Scanner(true, true), identifierMap);
79   //            identifierMap = null;
80   //            
81   //    // Eliminate redundancies in list of resources being exported
82   //    Iterator elementsEnum = resources.iterator();
83   //    while (elementsEnum.hasNext()) {
84   //      IResource currentResource = (IResource) elementsEnum.next();
85   //      if (isDescendent(resources, currentResource))
86   //        elementsEnum.remove(); //Remove currentResource
87   //    }
88   //
89   //    resourcesToExport = resources;
90   //    path = new Path(destinationPath);
91   //    overwriteCallback = overwriteImplementor;
92   //  }
93   /**
94    *  Create an instance of this class.  Use this constructor if you wish to
95    *  recursively export a single resource
96    */
97   public ObfuscatorExportOperation(IResource res, String destinationPath, IOverwriteQuery overwriteImplementor) {
98     super();
99
100     fResource = res;
101     fPath = new Path(destinationPath);
102     fOverwriteCallback = overwriteImplementor;
103   }
104   /**
105    *  Create an instance of this class.  Use this constructor if you wish to
106    *  export specific resources with a common parent resource (affects container
107    *  directory creation)
108    */
109   public ObfuscatorExportOperation(IResource res, List resources, String destinationPath, IOverwriteQuery overwriteImplementor) {
110     this(res, destinationPath, overwriteImplementor);
111     fResourcesToExport = resources;
112   }
113   /**
114    * Add a new entry to the error table with the passed information
115    */
116   protected void addError(String message, Throwable e) {
117     errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, message, e));
118   }
119   /**
120    *  Answer the total number of file resources that exist at or below self in the
121    *  resources hierarchy.
122    *
123    *  @return int
124    *  @param resource org.eclipse.core.resources.IResource
125    */
126   protected int countChildrenOf(IResource resource) throws CoreException {
127     if (resource.getType() == IResource.FILE)
128       return 1;
129
130     int count = 0;
131     if (resource.isAccessible()) {
132       IResource[] children = ((IContainer) resource).members();
133       for (int i = 0; i < children.length; i++)
134         count += countChildrenOf(children[i]);
135     }
136
137     return count;
138   }
139   /**
140    *    Answer a boolean indicating the number of file resources that were
141    *    specified for export
142    *
143    *    @return int
144    */
145   protected int countSelectedResources() throws CoreException {
146     int result = 0;
147     Iterator resources = fResourcesToExport.iterator();
148
149     while (resources.hasNext())
150       result += countChildrenOf((IResource) resources.next());
151
152     return result;
153   }
154   /**
155    *  Create the directories required for exporting the passed resource,
156    *  based upon its container hierarchy
157    *
158    *  @param resource org.eclipse.core.resources.IResource
159    */
160   protected void createLeadupDirectoriesFor(IResource resource) {
161     IPath resourcePath = resource.getFullPath().removeLastSegments(1);
162
163     for (int i = 0; i < resourcePath.segmentCount(); i++) {
164       fPath = fPath.append(resourcePath.segment(i));
165       fExporter2.createFolder(fPath);
166     }
167   }
168   /**
169    *    Recursively export the previously-specified resource
170    */
171   protected void exportAllResources1() throws InterruptedException {
172     if (fResource.getType() == IResource.FILE) {
173       exportFile1((IFile) fResource, fPath);
174     } else {
175       try {
176         setExporters(fResource);
177         exportChildren1(((IContainer) fResource).members(), fPath);
178       } catch (CoreException e) {
179         // not safe to show a dialog
180         // should never happen because the file system export wizard ensures that the
181         // single resource chosen for export is both existent and accessible
182         errorTable.add(e);
183       }
184     }
185   }
186   /**
187    *    Recursively export the previously-specified resource
188    */
189   protected void exportAllResources2() throws InterruptedException {
190     if (fResource.getType() == IResource.FILE) {
191       exportFile2((IFile) fResource, fPath);
192     } else {
193       try {
194         setExporters(fResource);
195         exportChildren2(((IContainer) fResource).members(), fPath);
196       } catch (CoreException e) {
197         // not safe to show a dialog
198         // should never happen because the file system export wizard ensures that the
199         // single resource chosen for export is both existent and accessible
200         errorTable.add(e);
201       }
202     }
203   }
204   /**
205    *    Export all of the resources contained in the passed collection
206    *
207    *    @param children java.util.Enumeration
208    *    @param currentPath IPath
209    */
210   protected void exportChildren1(IResource[] children, IPath currentPath) throws InterruptedException {
211     for (int i = 0; i < children.length; i++) {
212       IResource child = children[i];
213       if (!child.isAccessible())
214         continue;
215
216       if (child.getType() == IResource.FILE)
217         exportFile1((IFile) child, currentPath);
218       else {
219         IPath destination = currentPath.append(child.getName());
220         fExporter1.createFolder(destination);
221         try {
222           exportChildren1(((IContainer) child).members(), destination);
223         } catch (CoreException e) {
224           // not safe to show a dialog
225           // should never happen because:
226           // i. this method is called recursively iterating over the result of #members,
227           //            which only answers existing children
228           // ii. there is an #isAccessible check done before #members is invoked
229           errorTable.add(e.getStatus());
230         }
231       }
232     }
233   }
234
235   /**
236    *    Export all of the resources contained in the passed collection
237    *
238    *    @param children java.util.Enumeration
239    *    @param currentPath IPath
240    */
241   protected void exportChildren2(IResource[] children, IPath currentPath) throws InterruptedException {
242     for (int i = 0; i < children.length; i++) {
243       IResource child = children[i];
244       if (!child.isAccessible())
245         continue;
246
247       if (child.getType() == IResource.FILE)
248         exportFile2((IFile) child, currentPath);
249       else {
250         IPath destination = currentPath.append(child.getName());
251         fExporter2.createFolder(destination);
252         try {
253           exportChildren2(((IContainer) child).members(), destination);
254         } catch (CoreException e) {
255           // not safe to show a dialog
256           // should never happen because:
257           // i. this method is called recursively iterating over the result of #members,
258           //            which only answers existing children
259           // ii. there is an #isAccessible check done before #members is invoked
260           errorTable.add(e.getStatus());
261         }
262       }
263     }
264   }
265
266   protected void exportFile1(IFile file, IPath location) throws InterruptedException {
267     IPath fullPath = location.append(file.getName());
268     fMonitor.subTask(file.getFullPath().toString());
269     String properPathString = fullPath.toOSString();
270     File targetFile = new File(properPathString);
271
272     //    if (targetFile.exists()) {
273     //      if (!targetFile.canWrite()) {
274     //        errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, ObfuscatorExportMessages.format("ObfuscatorTransfer.cannotOverwrite", //$NON-NLS-1$
275     //        new Object[] { targetFile.getAbsolutePath()}), null));
276     //        monitor.worked(1);
277     //        return;
278     //      }
279     //
280     //      if (overwriteState == OVERWRITE_NONE)
281     //        return;
282     //
283     //      if (overwriteState != OVERWRITE_ALL) {
284     //        String overwriteAnswer = overwriteCallback.queryOverwrite(properPathString);
285     //
286     //        if (overwriteAnswer.equals(IOverwriteQuery.CANCEL))
287     //          throw new InterruptedException();
288     //
289     //        if (overwriteAnswer.equals(IOverwriteQuery.NO)) {
290     //          monitor.worked(1);
291     //          return;
292     //        }
293     //
294     //        if (overwriteAnswer.equals(IOverwriteQuery.NO_ALL)) {
295     //          monitor.worked(1);
296     //          overwriteState = OVERWRITE_NONE;
297     //          return;
298     //        }
299     //
300     //        if (overwriteAnswer.equals(IOverwriteQuery.ALL))
301     //          overwriteState = OVERWRITE_ALL;
302     //      }
303     //    }
304
305     try {
306       setExporters(file);
307       fExporter1.write(file, fullPath);
308     } catch (IOException e) {
309       errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, ObfuscatorExportMessages.format("ObfuscatorTransfer.errorExporting", //$NON-NLS-1$
310       new Object[] { fullPath, e.getMessage()}), e));
311     } catch (CoreException e) {
312       errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, ObfuscatorExportMessages.format("ObfuscatorTransfer.errorExporting", //$NON-NLS-1$
313       new Object[] { fullPath, e.getMessage()}), e));
314     }
315
316     fMonitor.worked(1);
317     ModalContext.checkCanceled(fMonitor);
318   }
319   /**
320    *  Export the passed file to the specified location
321    *
322    *  @param file org.eclipse.core.resources.IFile
323    *  @param location org.eclipse.core.runtime.IPath
324    */
325   protected void exportFile2(IFile file, IPath location) throws InterruptedException {
326     IPath fullPath = location.append(file.getName());
327     fMonitor.subTask(file.getFullPath().toString());
328     String properPathString = fullPath.toOSString();
329     File targetFile = new File(properPathString);
330
331     if (targetFile.exists()) {
332       if (!targetFile.canWrite()) {
333         errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, ObfuscatorExportMessages.format("ObfuscatorTransfer.cannotOverwrite", //$NON-NLS-1$
334         new Object[] { targetFile.getAbsolutePath()}), null));
335         fMonitor.worked(1);
336         return;
337       }
338
339       if (overwriteState == OVERWRITE_NONE)
340         return;
341
342       if (overwriteState != OVERWRITE_ALL) {
343         String overwriteAnswer = fOverwriteCallback.queryOverwrite(properPathString);
344
345         if (overwriteAnswer.equals(IOverwriteQuery.CANCEL))
346           throw new InterruptedException();
347
348         if (overwriteAnswer.equals(IOverwriteQuery.NO)) {
349           fMonitor.worked(1);
350           return;
351         }
352
353         if (overwriteAnswer.equals(IOverwriteQuery.NO_ALL)) {
354           fMonitor.worked(1);
355           overwriteState = OVERWRITE_NONE;
356           return;
357         }
358
359         if (overwriteAnswer.equals(IOverwriteQuery.ALL))
360           overwriteState = OVERWRITE_ALL;
361       }
362     }
363
364     try {
365       setExporters(file);
366       fExporter2.write(file, fullPath);
367     } catch (IOException e) {
368       errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, ObfuscatorExportMessages.format("ObfuscatorTransfer.errorExporting", //$NON-NLS-1$
369       new Object[] { fullPath, e.getMessage()}), e));
370     } catch (CoreException e) {
371       errorTable.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, ObfuscatorExportMessages.format("ObfuscatorTransfer.errorExporting", //$NON-NLS-1$
372       new Object[] { fullPath, e.getMessage()}), e));
373     }
374
375     fMonitor.worked(1);
376     ModalContext.checkCanceled(fMonitor);
377   }
378
379   protected void exportSpecifiedResources1() throws InterruptedException {
380     Iterator resources = fResourcesToExport.iterator();
381     IPath initPath = (IPath) fPath.clone();
382
383     while (resources.hasNext()) {
384       IResource currentResource = (IResource) resources.next();
385       if (!currentResource.isAccessible())
386         continue;
387       setExporters(currentResource);
388       fPath = initPath;
389
390       if (fResource == null) {
391         // No root resource specified and creation of containment directories
392         // is required.  Create containers from depth 2 onwards (ie.- project's
393         // child inclusive) for each resource being exported.
394         //        if (createLeadupStructure)
395         //          createLeadupDirectoriesFor(currentResource);
396
397       } else {
398         // Root resource specified.  Must create containment directories
399         // from this point onwards for each resource being exported
400         IPath containersToCreate =
401           currentResource.getFullPath().removeFirstSegments(fResource.getFullPath().segmentCount()).removeLastSegments(1);
402
403         for (int i = 0; i < containersToCreate.segmentCount(); i++) {
404           fPath = fPath.append(containersToCreate.segment(i));
405           fExporter1.createFolder(fPath);
406         }
407       }
408
409       if (currentResource.getType() == IResource.FILE)
410         exportFile1((IFile) currentResource, fPath);
411       else {
412         if (createContainerDirectories) {
413           fPath = fPath.append(currentResource.getName());
414           fExporter1.createFolder(fPath);
415         }
416
417         try {
418           exportChildren1(((IContainer) currentResource).members(), fPath);
419         } catch (CoreException e) {
420           // should never happen because #isAccessible is called before #members is invoked,
421           // which implicitly does an existence check
422           errorTable.add(e.getStatus());
423         }
424       }
425     }
426   }
427   /**
428    *    Export the resources contained in the previously-defined
429    *    resourcesToExport collection
430    */
431   protected void exportSpecifiedResources2() throws InterruptedException {
432     Iterator resources = fResourcesToExport.iterator();
433     IPath initPath = (IPath) fPath.clone();
434
435     while (resources.hasNext()) {
436       IResource currentResource = (IResource) resources.next();
437       if (!currentResource.isAccessible())
438         continue;
439       setExporters(currentResource);
440
441       fPath = initPath;
442
443       if (fResource == null) {
444         // No root resource specified and creation of containment directories
445         // is required.  Create containers from depth 2 onwards (ie.- project's
446         // child inclusive) for each resource being exported.
447         //        if (createLeadupStructure)
448         //          createLeadupDirectoriesFor(currentResource);
449
450       } else {
451         // Root resource specified.  Must create containment directories
452         // from this point onwards for each resource being exported
453         IPath containersToCreate =
454           currentResource.getFullPath().removeFirstSegments(fResource.getFullPath().segmentCount()).removeLastSegments(1);
455
456         for (int i = 0; i < containersToCreate.segmentCount(); i++) {
457           fPath = fPath.append(containersToCreate.segment(i));
458           fExporter2.createFolder(fPath);
459         }
460       }
461
462       if (currentResource.getType() == IResource.FILE)
463         exportFile2((IFile) currentResource, fPath);
464       else {
465         if (createContainerDirectories) {
466           fPath = fPath.append(currentResource.getName());
467           fExporter2.createFolder(fPath);
468         }
469
470         try {
471           exportChildren2(((IContainer) currentResource).members(), fPath);
472         } catch (CoreException e) {
473           // should never happen because #isAccessible is called before #members is invoked,
474           // which implicitly does an existence check
475           errorTable.add(e.getStatus());
476         }
477       }
478     }
479   }
480   /**
481    * Returns the status of the export operation.
482    * If there were any errors, the result is a status object containing
483    * individual status objects for each error.
484    * If there were no errors, the result is a status object with error code <code>OK</code>.
485    *
486    * @return the status
487    */
488   public IStatus getStatus() {
489     IStatus[] errors = new IStatus[errorTable.size()];
490     errorTable.toArray(errors);
491     return new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK, errors, ObfuscatorExportMessages.getString("ObfuscatorExportOperation.problemsExporting"), //$NON-NLS-1$
492     null);
493   }
494   /**
495    *  Answer a boolean indicating whether the passed child is a descendent
496    *  of one or more members of the passed resources collection
497    *
498    *  @return boolean
499    *  @param resources java.util.List
500    *  @param child org.eclipse.core.resources.IResource
501    */
502   protected boolean isDescendent(List resources, IResource child) {
503     if (child.getType() == IResource.PROJECT)
504       return false;
505
506     IResource parent = child.getParent();
507     if (resources.contains(parent))
508       return true;
509
510     return isDescendent(resources, parent);
511   }
512
513   private void setExporters(IResource resource) {
514     if (fCurrentIdentifierMap == null) {
515       if (fProjectMap == null) {
516         fProjectMap = new HashMap();
517       }
518       createExporters(resource);
519     } else {
520       IProject project = resource.getProject();
521       if (!fCurrentProjectName.equals(project.getName())) {
522         HashMap temp = (HashMap) fProjectMap.get(project.getName());
523         if (temp != null) {
524           fCurrentProjectName = project.getName();
525           fCurrentIdentifierMap = temp;
526           fExporter1 = new ObfuscatorPass1Exporter(new Scanner(false, false), fCurrentIdentifierMap);
527           fExporter2 = new ObfuscatorPass2Exporter(new Scanner(true, true), fCurrentIdentifierMap);
528           return;
529         }
530         createExporters(resource);
531       }
532     }
533   }
534
535   private void createExporters(IResource resource) {
536     IProject project = resource.getProject();
537     IPreferenceStore store = PHPeclipsePlugin.getDefault().getPreferenceStore();
538     ObfuscatorIgnores ignore = new ObfuscatorIgnores(project);
539     fCurrentIdentifierMap = ignore.getIdentifierMap();
540     fCurrentProjectName = project.getName();
541     fProjectMap.put(fCurrentProjectName, fCurrentIdentifierMap);
542     fExporter1 = new ObfuscatorPass1Exporter(new Scanner(false, false), fCurrentIdentifierMap);
543     fExporter2 = new ObfuscatorPass2Exporter(new Scanner(true, true), fCurrentIdentifierMap);
544   }
545   /**
546    *    Export the resources that were previously specified for export
547    *    (or if a single resource was specified then export it recursively)
548    */
549   public void run(IProgressMonitor monitor) throws InterruptedException {
550     this.fMonitor = monitor;
551     final IPath tempPath = (IPath) fPath.clone();
552     if (fResource != null) {
553       setExporters(fResource);
554       //      if (createLeadupStructure)
555       //        createLeadupDirectoriesFor(resource);
556
557       if (createContainerDirectories && fResource.getType() != IResource.FILE) {
558         // ensure it's a container
559         fPath = fPath.append(fResource.getName());
560         fExporter2.createFolder(fPath);
561       }
562     }
563
564     try {
565         // reset variables for this run:
566         fCurrentIdentifierMap = null;
567         fProjectMap = null;
568         fCurrentProjectName = "";
569         
570         // count number of files
571       int totalWork = IProgressMonitor.UNKNOWN;
572       try {
573         if (fResourcesToExport == null) {
574           totalWork = countChildrenOf(fResource);
575         } else {
576           totalWork = countSelectedResources();
577         }
578       } catch (CoreException e) {
579         // Should not happen
580         errorTable.add(e.getStatus());
581       }
582       monitor.beginTask(ObfuscatorExportMessages.getString("ObfuscatorTransfer.exportingTitle1"), totalWork); //$NON-NLS-1$
583       if (fResourcesToExport == null) {
584         exportAllResources1();
585       } else {
586         exportSpecifiedResources1();
587       }
588
589       //      try {
590       //        if (resourcesToExport == null)
591       //          totalWork = countChildrenOf(resource);
592       //        else
593       //          totalWork = countSelectedResources();
594       //      } catch (CoreException e) {
595       //        // Should not happen
596       //        errorTable.add(e.getStatus());
597       //      }
598
599       // reset path:
600       fPath = tempPath;
601       monitor.beginTask(ObfuscatorExportMessages.getString("ObfuscatorTransfer.exportingTitle2"), totalWork); //$NON-NLS-1$
602       if (fResourcesToExport == null) {
603         exportAllResources2();
604       } else {
605         exportSpecifiedResources2();
606       }
607     } finally {
608       monitor.done();
609     }
610   }
611   /**
612    *    Set this boolean indicating whether a directory should be created for
613    *    Folder resources that are explicitly passed for export
614    *
615    *    @param value boolean
616    */
617   //  public void setCreateContainerDirectories(boolean value) {
618   //    createContainerDirectories = value;
619   //  }
620   /**
621    *    Set this boolean indicating whether each exported resource's complete path should
622    *    include containment hierarchies as dictated by its parents
623    *
624    *    @param value boolean
625    */
626   //  public void setCreateLeadupStructure(boolean value) {
627   //    createLeadupStructure = value;
628   //  }
629   /**
630    *    Set this boolean indicating whether exported resources should automatically
631    *    overwrite existing files when a conflict occurs. If not
632    *    query the user.
633    *
634    *    @param value boolean
635    */
636   public void setOverwriteFiles(boolean value) {
637     if (value)
638       overwriteState = OVERWRITE_ALL;
639   }
640 }