Home | History | Annotate | Download | only in actions
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ide.eclipse.adt.internal.actions;
     18 
     19 import com.android.SdkConstants;
     20 import com.android.annotations.Nullable;
     21 import com.android.ide.eclipse.adt.AdtConstants;
     22 import com.android.ide.eclipse.adt.AdtPlugin;
     23 import com.android.ide.eclipse.adt.AdtUtils;
     24 import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
     25 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
     26 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
     27 import com.android.sdklib.SdkManager;
     28 import com.android.sdklib.internal.project.ProjectProperties;
     29 import com.android.sdklib.internal.project.ProjectProperties.PropertyType;
     30 import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
     31 import com.android.sdklib.io.FileOp;
     32 import com.android.sdkuilib.internal.repository.ui.AdtUpdateDialog;
     33 import com.android.utils.NullLogger;
     34 import com.android.utils.Pair;
     35 
     36 import org.eclipse.core.filesystem.EFS;
     37 import org.eclipse.core.filesystem.IFileStore;
     38 import org.eclipse.core.filesystem.IFileSystem;
     39 import org.eclipse.core.resources.IFile;
     40 import org.eclipse.core.resources.IFolder;
     41 import org.eclipse.core.resources.IProject;
     42 import org.eclipse.core.resources.IProjectDescription;
     43 import org.eclipse.core.resources.IResource;
     44 import org.eclipse.core.resources.IWorkspace;
     45 import org.eclipse.core.resources.IWorkspaceRoot;
     46 import org.eclipse.core.resources.ResourcesPlugin;
     47 import org.eclipse.core.runtime.CoreException;
     48 import org.eclipse.core.runtime.IAdaptable;
     49 import org.eclipse.core.runtime.IPath;
     50 import org.eclipse.core.runtime.IProgressMonitor;
     51 import org.eclipse.core.runtime.IStatus;
     52 import org.eclipse.core.runtime.NullProgressMonitor;
     53 import org.eclipse.core.runtime.Status;
     54 import org.eclipse.core.runtime.jobs.Job;
     55 import org.eclipse.jdt.core.IJavaProject;
     56 import org.eclipse.jdt.core.JavaCore;
     57 import org.eclipse.jface.action.IAction;
     58 import org.eclipse.jface.viewers.ISelection;
     59 import org.eclipse.jface.viewers.IStructuredSelection;
     60 import org.eclipse.ui.IObjectActionDelegate;
     61 import org.eclipse.ui.IWorkbenchPart;
     62 import org.eclipse.ui.IWorkbenchWindow;
     63 import org.eclipse.ui.IWorkbenchWindowActionDelegate;
     64 
     65 import java.io.File;
     66 import java.io.IOException;
     67 import java.util.Iterator;
     68 import java.util.Map;
     69 
     70 /**
     71  * An action to add the android-support-v4.jar support library
     72  * to the selected project.
     73  * <p/>
     74  * This should be used by the GLE. The action itself is currently more
     75  * like an example of how to invoke the new {@link AdtUpdateDialog}.
     76  * <p/>
     77  * TODO: make this more configurable.
     78  */
     79 public class AddSupportJarAction implements IObjectActionDelegate {
     80 
     81     /** The vendor ID of the support library. */
     82     private static final String VENDOR_ID = "android";                             //$NON-NLS-1$
     83     /** The path ID of the support library. */
     84     private static final String SUPPORT_ID = "support";                            //$NON-NLS-1$
     85     /** The path ID of the compatibility library (which was its id for releases 1-3). */
     86     private static final String COMPATIBILITY_ID = "compatibility";                //$NON-NLS-1$
     87     private static final String FD_GRIDLAYOUT = "gridlayout";                      //$NON-NLS-1$
     88     private static final String FD_V7 = "v7";                                      //$NON-NLS-1$
     89     private static final String FD_V4 = "v4";                                      //$NON-NLS-1$
     90     private static final String FD_V13 = "v13";                                    //$NON-NLS-1$
     91     private static final String FD_APPCOMPAT = "appcompat";                        //$NON-NLS-1$
     92     private static final String FD_LIBS = "libs";                                  //$NON-NLS-1$
     93     private static final String ANDROID_SUPPORT_V4_JAR = "android-support-v4.jar"; //$NON-NLS-1$
     94     private static final String ANDROID_SUPPORT_V13_JAR = "android-support-v13.jar";//$NON-NLS-1$
     95     private static final String APPCOMPAT_V7_JAR = "android-support-v7-appcompat.jar";//$NON-NLS-1$
     96     private static final String APP_COMPAT_LIB_NAME = "appcompat_v7";               //$NON-NLS-1$
     97     private ISelection mSelection;
     98 
     99     /**
    100      * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
    101      */
    102     @Override
    103     public void setActivePart(IAction action, IWorkbenchPart targetPart) {
    104     }
    105 
    106     @Override
    107     public void run(IAction action) {
    108         if (mSelection instanceof IStructuredSelection) {
    109 
    110             for (Iterator<?> it = ((IStructuredSelection) mSelection).iterator();
    111                     it.hasNext();) {
    112                 Object element = it.next();
    113                 IProject project = null;
    114                 if (element instanceof IProject) {
    115                     project = (IProject) element;
    116                 } else if (element instanceof IAdaptable) {
    117                     project = (IProject) ((IAdaptable) element)
    118                             .getAdapter(IProject.class);
    119                 }
    120                 if (project != null) {
    121                     install(project);
    122                 }
    123             }
    124         }
    125     }
    126 
    127     @Override
    128     public void selectionChanged(IAction action, ISelection selection) {
    129         mSelection = selection;
    130     }
    131 
    132     /**
    133      * Install the support jar into the given project.
    134      *
    135      * @param project The Android project to install the support jar into
    136      * @return true if the installation was successful
    137      */
    138     public static boolean install(final IProject project) {
    139         File jarPath = installSupport(-1);
    140         if (jarPath != null) {
    141             try {
    142                 return copyJarIntoProject(project, jarPath) != null;
    143             } catch (Exception e) {
    144                 AdtPlugin.log(e, null);
    145             }
    146         }
    147 
    148         return false;
    149     }
    150 
    151     /**
    152      * Installs the Android Support library into the SDK extras/ folder. If a minimum
    153      * revision number is specified, this method will check whether the package is already
    154      * installed, and if the installed revision is at least as high as the requested revision,
    155      * this method will exit without performing an update.
    156      *
    157      * @param minimumRevision a minimum revision, or -1 to upgrade
    158      *            unconditionally. Note that this does <b>NOT</b> specify which
    159      *            revision to install; the latest version will always be
    160      *            installed.
    161      * @return the location of the support jar file, or null if something went
    162      *            wrong
    163      */
    164     @Nullable
    165     public static File installSupport(int minimumRevision) {
    166 
    167         final Sdk sdk = Sdk.getCurrent();
    168         if (sdk == null) {
    169             AdtPlugin.printErrorToConsole(
    170                     AddSupportJarAction.class.getSimpleName(),   // tag
    171                     "Error: Android SDK is not loaded yet."); //$NON-NLS-1$
    172             return null;
    173         }
    174 
    175         String sdkLocation = sdk.getSdkOsLocation();
    176         if (minimumRevision > 0) {
    177             File path = getSupportJarFile();
    178             if (path != null) {
    179                 assert path.exists(); // guaranteed by the getSupportJarFile call
    180                 int installedRevision = getInstalledRevision();
    181                 if (installedRevision != -1 && minimumRevision <= installedRevision) {
    182                     return path;
    183                 }
    184             }
    185         }
    186 
    187         // TODO: For the generic action, check the library isn't in the project already.
    188 
    189         // First call the package manager to make sure the package is installed
    190         // and get the installation path of the library.
    191 
    192         AdtUpdateDialog window = new AdtUpdateDialog(
    193                 AdtPlugin.getShell(),
    194                 new AdtConsoleSdkLog(),
    195                 sdkLocation);
    196 
    197         Pair<Boolean, File> result = window.installExtraPackage(VENDOR_ID, SUPPORT_ID);
    198 
    199         // TODO: Make sure the version is at the required level; we know we need at least one
    200         // containing the v7 support
    201 
    202         if (!result.getFirst().booleanValue()) {
    203             AdtPlugin.printErrorToConsole("Failed to install Android Support library");
    204             return null;
    205         }
    206 
    207         // TODO these "v4" values needs to be dynamic, e.g. we could try to match
    208         // vN/android-support-vN.jar. Eventually we'll want to rely on info from the
    209         // package manifest anyway so this is irrelevant.
    210 
    211         File path = new File(result.getSecond(), FD_V4);
    212         final File jarPath = new File(path, ANDROID_SUPPORT_V4_JAR);
    213 
    214         if (!jarPath.isFile()) {
    215             AdtPlugin.printErrorToConsole("Android Support Jar not found:",
    216                     jarPath.getAbsolutePath());
    217             return null;
    218         }
    219 
    220         return jarPath;
    221     }
    222 
    223     /**
    224      * Returns the installed revision number of the Android Support
    225      * library, or -1 if the package is not installed.
    226      *
    227      * @return the installed revision number, or -1
    228      */
    229     public static int getInstalledRevision() {
    230         final Sdk sdk = Sdk.getCurrent();
    231         if (sdk != null) {
    232             String sdkLocation = sdk.getSdkOsLocation();
    233             SdkManager manager = SdkManager.createManager(sdkLocation, NullLogger.getLogger());
    234             Map<String, Integer> versions = manager.getExtrasVersions();
    235             Integer version = versions.get(VENDOR_ID + '/' + SUPPORT_ID);
    236             if (version == null) {
    237                 // Check the old compatibility library. When the library is updated in-place
    238                 // the manager doesn't change its folder name (since that is a source of
    239                 // endless issues on Windows.)
    240                 version = versions.get(VENDOR_ID + '/' + COMPATIBILITY_ID);
    241             }
    242             if (version != null) {
    243                 return version.intValue();
    244             }
    245         }
    246 
    247         return -1;
    248     }
    249 
    250     /**
    251      * Similar to {@link #install}, but rather than copy a jar into the given
    252      * project, it creates a new library project in the workspace for the
    253      * support library, and adds a library dependency on the newly
    254      * installed library from the given project.
    255      *
    256      * @param project the project to add a dependency on the library to
    257      * @param waitForFinish If true, block until the task has finished
    258      * @return true if the installation was successful (or if
    259      *         <code>waitForFinish</code> is false, if the installation is
    260      *         likely to be successful - e.g. the user has at least agreed to
    261      *         all installation prompts.)
    262      */
    263     public static boolean installGridLayoutLibrary(final IProject project, boolean waitForFinish) {
    264         final IJavaProject javaProject = JavaCore.create(project);
    265         if (javaProject != null) {
    266 
    267             File supportPath = getSupportPackageDir();
    268             if (!supportPath.isDirectory()) {
    269                 File path = installSupport(8); // GridLayout arrived in rev 7 and fixed in rev 8
    270                 if (path == null) {
    271                     return false;
    272                 }
    273                 assert path.equals(supportPath);
    274             }
    275             File libraryPath = new File(supportPath, FD_V7 + File.separator + FD_GRIDLAYOUT);
    276             if (!libraryPath.isDirectory()) {
    277                 // Upgrade support package: it's out of date. The SDK manager will
    278                 // perform an upgrade to the latest version if the package is already installed.
    279                 File path = installSupport(-1);
    280                 if (path == null) {
    281                     return false;
    282                 }
    283                 assert path.equals(libraryPath) : path;
    284             }
    285 
    286             // Create workspace copy of the project and add library dependency
    287             IProject libraryProject = createLibraryProject(libraryPath, project,
    288                     "gridlayout_v7", waitForFinish); //$NON-NLS-1$
    289             if (libraryProject != null) {
    290                 return addLibraryDependency(libraryProject, project, waitForFinish);
    291             }
    292         }
    293 
    294         return false;
    295     }
    296 
    297     /**
    298      * Similar to {@link #install}, but rather than copy a jar into the given
    299      * project, it creates a new library project in the workspace for the
    300      * support library, and adds a library dependency on the newly
    301      * installed library from the given project.
    302      *
    303      * @param project the project to add a dependency on the library to
    304      * @param waitForFinish If true, block until the task has finished
    305      * @return true if the installation was successful (or if
    306      *         <code>waitForFinish</code> is false, if the installation is
    307      *         likely to be successful - e.g. the user has at least agreed to
    308      *         all installation prompts.)
    309      */
    310     public static boolean installAppCompatLibrary(final IProject project, boolean waitForFinish) {
    311         final IJavaProject javaProject = JavaCore.create(project);
    312         if (javaProject != null) {
    313 
    314             // Don't add in the library if it already exists
    315             ProjectState state = Sdk.getProjectState(project);
    316             ProjectPropertiesWorkingCopy copy = state.getProperties().makeWorkingCopy();
    317             for (String property : copy.keySet()) {
    318                 if (property.startsWith(ProjectProperties.PROPERTY_LIB_REF)) {
    319                     String libraryReference = copy.getProperty(property);
    320                     if (libraryReference != null && libraryReference.contains(APP_COMPAT_LIB_NAME)) {
    321                         return true;
    322                     }
    323                 }
    324             }
    325 
    326             File supportPath = getSupportPackageDir();
    327             if (!supportPath.isDirectory()) {
    328                 File path = installSupport(7);
    329                 if (path == null) {
    330                     return false;
    331                 }
    332                 assert path.equals(supportPath);
    333             }
    334             File libraryPath = new File(supportPath, FD_V7 + File.separator + FD_APPCOMPAT);
    335             if (!libraryPath.isDirectory()) {
    336                 // Upgrade support package: it's out of date. The SDK manager will
    337                 // perform an upgrade to the latest version if the package is already installed.
    338                 File path = installSupport(-1);
    339                 if (path == null) {
    340                     return false;
    341                 }
    342                 assert path.equals(libraryPath) : path;
    343             }
    344 
    345             // Check to see if there's already a version of the library available
    346             IWorkspace workspace = ResourcesPlugin.getWorkspace();
    347             IWorkspaceRoot root = workspace.getRoot();
    348             IProject libraryProject = root.getProject(APP_COMPAT_LIB_NAME);
    349             if (!libraryProject.exists()) {
    350             	// Create workspace copy of the project and add library dependency
    351             	libraryProject = createLibraryProject(libraryPath, project,
    352             			APP_COMPAT_LIB_NAME, waitForFinish);
    353             }
    354             if (libraryProject != null) {
    355                 return addLibraryDependency(libraryProject, project, waitForFinish);
    356             }
    357         }
    358 
    359         return false;
    360     }
    361 
    362     /**
    363      * Returns the directory containing the support libraries (v4, v7, v13,
    364      * ...), which may or may not exist
    365      *
    366      * @return a path to the support library or null
    367      */
    368     private static File getSupportPackageDir() {
    369         final Sdk sdk = Sdk.getCurrent();
    370         if (sdk != null) {
    371             String sdkLocation = sdk.getSdkOsLocation();
    372             SdkManager manager = SdkManager.createManager(sdkLocation, NullLogger.getLogger());
    373             Map<String, Integer> versions = manager.getExtrasVersions();
    374             Integer version = versions.get(VENDOR_ID + '/' + SUPPORT_ID);
    375             if (version != null) {
    376                 File supportPath = new File(sdkLocation,
    377                         SdkConstants.FD_EXTRAS + File.separator
    378                         + VENDOR_ID + File.separator
    379                         + SUPPORT_ID);
    380                 return supportPath;
    381             }
    382 
    383             // Check the old compatibility library. When the library is updated in-place
    384             // the manager doesn't change its folder name (since that is a source of
    385             // endless issues on Windows.)
    386             version = versions.get(VENDOR_ID + '/' + COMPATIBILITY_ID);
    387             if (version != null) {
    388                 File supportPath = new File(sdkLocation,
    389                         SdkConstants.FD_EXTRAS + File.separator
    390                         + VENDOR_ID + File.separator
    391                         + COMPATIBILITY_ID);
    392                 return supportPath;
    393             }
    394         }
    395         return null;
    396     }
    397 
    398     /**
    399      * Returns a path to the installed jar file for the support library,
    400      * or null if it does not exist
    401      *
    402      * @return a path to the v4.jar or null
    403      */
    404     @Nullable
    405     public static File getSupportJarFile() {
    406         File supportDir = getSupportPackageDir();
    407         if (supportDir != null) {
    408             File path = new File(supportDir, FD_V4 + File.separator + ANDROID_SUPPORT_V4_JAR);
    409             if (path.exists()) {
    410                 return path;
    411             }
    412         }
    413 
    414         return null;
    415     }
    416 
    417     /**
    418      * Returns a path to the installed jar file for the support library,
    419      * or null if it does not exist
    420      *
    421      * @return a path to the v13.jar or null
    422      */
    423     @Nullable
    424     public static File getSupport13JarFile() {
    425         File supportDir = getSupportPackageDir();
    426         if (supportDir != null) {
    427             File path = new File(supportDir, FD_V13 + File.separator + ANDROID_SUPPORT_V13_JAR);
    428             if (path.exists()) {
    429                 return path;
    430             }
    431         }
    432 
    433         return null;
    434     }
    435 
    436     /**
    437      * Creates a library project in the Eclipse workspace out of the grid layout project
    438      * in the SDK tree.
    439      *
    440      * @param libraryPath the path to the directory tree containing the project contents
    441      * @param project the project to copy the SDK target out of
    442      * @param waitForFinish whether the operation should finish before this method returns
    443      * @return a library project, or null if it fails for some reason
    444      */
    445     private static IProject createLibraryProject(
    446             final File libraryPath,
    447             final IProject project,
    448             final String libraryName,
    449             boolean waitForFinish) {
    450 
    451         // Install a new library into the workspace. This is a copy rather than
    452         // a reference to the support library version such that modifications
    453         // do not modify the pristine copy in the SDK install area.
    454 
    455         final IProject newProject;
    456         try {
    457             IProgressMonitor monitor = new NullProgressMonitor();
    458             IWorkspace workspace = ResourcesPlugin.getWorkspace();
    459             IWorkspaceRoot root = workspace.getRoot();
    460 
    461             String name = AdtUtils.getUniqueProjectName(
    462                     libraryName, "_"); //$NON-NLS-1$
    463             newProject = root.getProject(name);
    464             IProjectDescription description = workspace.newProjectDescription(name);
    465             String[] natures = new String[] { AdtConstants.NATURE_DEFAULT, JavaCore.NATURE_ID };
    466             description.setNatureIds(natures);
    467             newProject.create(description, monitor);
    468 
    469             // Copy in the files recursively
    470             IFileSystem fileSystem = EFS.getLocalFileSystem();
    471             IFileStore sourceDir = fileSystem.getStore(libraryPath.toURI());
    472             IFileStore destDir = fileSystem.getStore(newProject.getLocationURI());
    473             sourceDir.copy(destDir, EFS.OVERWRITE, null);
    474 
    475             // Make sure the src folder exists
    476             destDir.getChild(SdkConstants.SRC_FOLDER).mkdir(0, null /*monitor*/);
    477 
    478             // Set the android platform to the same level as the calling project
    479             ProjectState state = Sdk.getProjectState(project);
    480             String target = state.getProperties().getProperty(ProjectProperties.PROPERTY_TARGET);
    481             if (target != null && target.length() > 0) {
    482                 ProjectProperties properties = ProjectProperties.load(
    483                         destDir.toLocalFile(EFS.NONE, new NullProgressMonitor()).getPath(),
    484                         PropertyType.PROJECT);
    485                 ProjectPropertiesWorkingCopy copy = properties.makeWorkingCopy();
    486                 copy.setProperty(ProjectProperties.PROPERTY_TARGET, target);
    487                 try {
    488                     copy.save();
    489                 } catch (Exception e) {
    490                     AdtPlugin.log(e, null);
    491                 }
    492             }
    493 
    494             newProject.open(monitor);
    495 
    496             return newProject;
    497         } catch (CoreException e) {
    498             AdtPlugin.log(e, null);
    499             return null;
    500         }
    501     }
    502 
    503     /**
    504      * Adds a library dependency on the given library into the given project.
    505      *
    506      * @param libraryProject the library project to depend on
    507      * @param dependentProject the project to write the dependency into
    508      * @param waitForFinish whether this method should wait for the job to
    509      *            finish
    510      * @return true if the operation succeeded
    511      */
    512     public static boolean addLibraryDependency(
    513             final IProject libraryProject,
    514             final IProject dependentProject,
    515             boolean waitForFinish) {
    516 
    517         // Now add library dependency
    518 
    519         // Run an Eclipse asynchronous job to update the project
    520         Job job = new Job("Add Support Library Dependency to Project") {
    521             @Override
    522             protected IStatus run(IProgressMonitor monitor) {
    523                 try {
    524                     monitor.beginTask("Add library dependency to project build path", 3);
    525                     monitor.worked(1);
    526 
    527                     // TODO: Add library project to the project.properties file!
    528                     ProjectState state = Sdk.getProjectState(dependentProject);
    529                     ProjectPropertiesWorkingCopy mPropertiesWorkingCopy =
    530                             state.getProperties().makeWorkingCopy();
    531 
    532                     // Get the highest version number of the libraries; there cannot be any
    533                     // gaps so we will assign the next library the next number
    534                     int nextVersion = 1;
    535                     for (String property : mPropertiesWorkingCopy.keySet()) {
    536                         if (property.startsWith(ProjectProperties.PROPERTY_LIB_REF)) {
    537                             String s = property.substring(
    538                                     ProjectProperties.PROPERTY_LIB_REF.length());
    539                             int version = Integer.parseInt(s);
    540                             if (version >= nextVersion) {
    541                                 nextVersion = version + 1;
    542                             }
    543                         }
    544                     }
    545 
    546                     IPath relativePath = libraryProject.getLocation().makeRelativeTo(
    547                             dependentProject.getLocation());
    548 
    549                     mPropertiesWorkingCopy.setProperty(
    550                             ProjectProperties.PROPERTY_LIB_REF + nextVersion,
    551                             relativePath.toString());
    552                     try {
    553                         mPropertiesWorkingCopy.save();
    554                         IResource projectProp = dependentProject.findMember(
    555                                 SdkConstants.FN_PROJECT_PROPERTIES);
    556                         projectProp.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
    557                     } catch (Exception e) {
    558                         String msg = String.format(
    559                                 "Failed to save %1$s for project %2$s",
    560                                 SdkConstants.FN_PROJECT_PROPERTIES, dependentProject.getName());
    561                         AdtPlugin.log(e, msg);
    562                     }
    563 
    564                     // Project fix-ups
    565                     Job fix = FixProjectAction.createFixProjectJob(libraryProject);
    566                     fix.schedule();
    567                     fix.join();
    568 
    569                     monitor.worked(1);
    570 
    571                     return Status.OK_STATUS;
    572                 } catch (Exception e) {
    573                     return new Status(Status.ERROR, AdtPlugin.PLUGIN_ID, Status.ERROR,
    574                             "Failed", e); //$NON-NLS-1$
    575                 } finally {
    576                     if (monitor != null) {
    577                         monitor.done();
    578                     }
    579                 }
    580             }
    581         };
    582         job.schedule();
    583 
    584         if (waitForFinish) {
    585             try {
    586                 job.join();
    587                 return job.getState() == IStatus.OK;
    588             } catch (InterruptedException e) {
    589                 AdtPlugin.log(e, null);
    590             }
    591         }
    592 
    593         return true;
    594     }
    595 
    596     private static IResource copyJarIntoProject(
    597             IProject project,
    598             File jarPath) throws IOException, CoreException {
    599         IFolder resFolder = project.getFolder(SdkConstants.FD_NATIVE_LIBS);
    600         if (!resFolder.exists()) {
    601             resFolder.create(IResource.FORCE, true /*local*/, null);
    602         }
    603 
    604         IFile destFile = resFolder.getFile(jarPath.getName());
    605         IPath loc = destFile.getLocation();
    606         File destPath = loc.toFile();
    607 
    608         // Only modify the file if necessary so that we don't trigger unnecessary recompilations
    609         FileOp f = new FileOp();
    610         if (!f.isFile(destPath) || !f.isSameFile(jarPath, destPath)) {
    611             f.copyFile(jarPath, destPath);
    612             // Make sure Eclipse discovers java.io file changes
    613             resFolder.refreshLocal(1, new NullProgressMonitor());
    614         }
    615 
    616         return destFile;
    617     }
    618 
    619     /**
    620      * @see IWorkbenchWindowActionDelegate#init
    621      */
    622     public void init(IWorkbenchWindow window) {
    623         // pass
    624     }
    625 
    626 }
    627