/**
* This file Copyright (c) 2005-2008 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain Eclipse Public Licensed code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.syncing.ui.views;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import com.aptana.ide.core.io.IConnectionPoint;
import com.aptana.ide.core.io.efs.EFSUtils;
import com.aptana.ide.core.io.syncing.SyncState;
import com.aptana.ide.core.io.syncing.VirtualFileSyncPair;
import com.aptana.ide.syncing.core.Synchronizer;
import com.aptana.ide.syncing.core.events.ISyncEventHandler;
/**
* @author Kevin Sawicki (ksawicki@aptana.com)
* @author Michael Xia (mxia@aptana.com)
*/
public class SyncJob extends Job implements ISyncEventHandler
{
/**
* The feedback client.
*/
public static interface Client
{
/**
* Indicates the job started syncing the specified pair.
*
* @param item
* the pair being synced
*/
public void syncItem(VirtualFileSyncPair item);
/**
* Indicates a certain number of bytes has been synced for a specific pair.
*
* @param item
* the pair being synced
* @param bytes
* the number of bytes transferred
*/
public void syncProgress(VirtualFileSyncPair item, long bytes);
/**
* Indicates the specified pair has completed.
*
* @param item
* the completed pair
* @param allDone
* true if all pairs have been completed, false otherwise
*/
public void syncDone(VirtualFileSyncPair item, boolean allDone);
/**
* Indicates the specified pair has error during transfer.
*
* @param item
* the error pair
* @param allDone
* true if all pairs have been completed, false otherwise
*/
public void syncError(VirtualFileSyncPair item, boolean allDone);
}
/**
* BOTH
*/
public static final int BOTH = 0;
/**
* UPLOAD
*/
public static final int UPLOAD = 1;
/**
* DOWNLOAD
*/
public static final int DOWNLOAD = 2;
private static final String NAME = "Syncing"; //$NON-NLS-1$
private Synchronizer fSyncer;
private List<VirtualFileSyncPair> fPairs;
private int fDirection;
private boolean fDeleteRemote;
private boolean fDeleteLocal;
private List<VirtualFileSyncPair> fCompletedPairs;
private int fErrorPairs;
private IProgressMonitor progressMonitor;
private Client fClient;
/**
* Constructor.
*
* @param syncer
* the synchronizer
* @param pairs
* the list of pairs to sync
* @param direction
* the direction which the pairs should be synced (BOTH, UPLOAD, or DOWNLOAD)
* @param deleteRemote
* indicates if the remote files should be deleted
* @param deleteLocal
* indicates if the local files should be deleted
* @param client
* the client to receive feedbacks on the progress of syncing
*/
public SyncJob(Synchronizer syncer, List<VirtualFileSyncPair> pairs, int direction, boolean deleteRemote,
boolean deleteLocal, Client client)
{
super(NAME);
fSyncer = syncer;
fPairs = pairs;
fDirection = direction;
fDeleteRemote = deleteRemote;
fDeleteLocal = deleteLocal;
fClient = client;
fCompletedPairs = new ArrayList<VirtualFileSyncPair>();
sortPairs();
fSyncer.setEventHandler(this);
}
/**
* Returns an array of completed pairs.
*
* @return the array of completed pairs
*/
public VirtualFileSyncPair[] getCompletedPairs()
{
return fCompletedPairs.toArray(new VirtualFileSyncPair[fCompletedPairs.size()]);
}
/**
* Returns the total number of bytes for the entire transfer.
*
* @return the total number of bytes for the entire transfer
*/
public long getTotalTransferBytes()
{
long bytes = 0;
int size = fPairs.size();
for (int i = 0; i < size; ++i)
{
bytes += getTransferBytes(fPairs.get(i));
}
return bytes;
}
public long getTransferBytes(VirtualFileSyncPair pair)
{
switch (fDirection)
{
case BOTH:
int state = pair.getSyncState();
if (state == SyncState.ClientItemIsNewer || state == SyncState.ClientItemOnly
|| state == SyncState.ClientItemDeleted)
{
return pair.getSourceFile().fetchInfo().getLength();
}
if (state == SyncState.ServerItemIsNewer || state == SyncState.ServerItemOnly
|| state == SyncState.ServerItemDeleted)
{
return pair.getDestinationFile().fetchInfo().getLength();
}
return 0;
case UPLOAD:
return pair.getSourceFile().fetchInfo().getLength();
case DOWNLOAD:
return pair.getDestinationFile().fetchInfo().getLength();
}
return 0;
}
public int getErrorCount()
{
return fPairs.size() - fCompletedPairs.size();
}
/**
* @see com.aptana.ide.syncing.core.events.sync.ISyncEventHandler#syncContinue()
*/
public boolean syncContinue() {
return !progressMonitor.isCanceled();
}
/**
* @see com.aptana.ide.syncing.core.events.sync.ISyncEventHandler#syncDone(VirtualFileSyncPair)
*/
public void syncDone(VirtualFileSyncPair item)
{
if (progressMonitor.isCanceled())
{
return;
}
fCompletedPairs.add(item);
int syncState = item.getSyncState();
if (syncState == SyncState.ClientItemOnly && fDeleteLocal && (fDirection == BOTH || fDirection == DOWNLOAD))
{
item.setSyncState(SyncState.ClientItemDeleted);
}
else if (syncState == SyncState.ServerItemOnly && fDeleteRemote && (fDirection == BOTH || fDirection == UPLOAD))
{
item.setSyncState(SyncState.ServerItemDeleted);
}
if (fClient != null)
{
fClient.syncDone(item, isDone());
}
progressMonitor.worked(1);
}
/**
* @see com.aptana.ide.syncing.core.events.sync.ISyncEventHandler#syncErrorEvent(VirtualFileSyncPair, Exception)
*/
public boolean syncErrorEvent(VirtualFileSyncPair item, Exception e)
{
if (progressMonitor.isCanceled())
{
return false;
}
fErrorPairs++;
if (fClient != null)
{
fClient.syncError(item, isDone());
}
return true;
}
/**
* @see com.aptana.ide.syncing.core.events.sync.ISyncEventHandler#syncEvent(VirtualFileSyncPair, int, int)
*/
public boolean syncEvent(VirtualFileSyncPair item, int index, int totalItems)
{
if (progressMonitor.isCanceled())
{
return false;
}
if (fClient != null)
{
fClient.syncItem(item);
}
return true;
}
/**
* @see com.aptana.ide.syncing.core.events.sync.ISyncEventHandler#syncTransferring(VirtualFileSyncPair, long)
*/
public void syncTransferring(VirtualFileSyncPair item, long bytes)
{
if (progressMonitor.isCanceled())
{
return;
}
if (fClient != null)
{
fClient.syncProgress(item, bytes);
}
}
/**
* @see com.aptana.ide.syncing.core.events.sync.ISyncEventHandler#getFilesEvent(IConnectionPoint, String)
*/
public boolean getFilesEvent(IConnectionPoint manager, String path)
{
return true;
}
/**
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
protected IStatus run(IProgressMonitor monitor)
{
this.progressMonitor = monitor;
if (fPairs.size() == 0)
{
// nothing to be synced (likely because all files are skipped), so considers it finished
if (fClient != null)
{
fClient.syncDone(null, true);
}
}
else
{
monitor.beginTask("Syncing files", fPairs.size()); //$NON-NLS-1$
VirtualFileSyncPair[] pairs = fPairs.toArray(new VirtualFileSyncPair[fPairs.size()]);
switch (fDirection)
{
case BOTH:
fullSync(pairs);
break;
case UPLOAD:
upload(pairs);
break;
case DOWNLOAD:
download(pairs);
break;
}
}
return Status.OK_STATUS;
}
private boolean fullSync(VirtualFileSyncPair[] pairs)
{
return fSyncer.fullSyncAndDelete(pairs, fDeleteLocal, fDeleteRemote, this.progressMonitor);
}
private boolean download(VirtualFileSyncPair[] pairs)
{
try
{
fSyncer.downloadAndDelete(pairs, fDeleteLocal, this.progressMonitor);
return true;
}
catch (CoreException e)
{
}
return false;
}
private boolean upload(VirtualFileSyncPair[] pairs)
{
try
{
fSyncer.uploadAndDelete(pairs, fDeleteRemote, this.progressMonitor);
return true;
}
catch (CoreException e)
{
}
return false;
}
private boolean isDone()
{
return fCompletedPairs.size() + fErrorPairs == fPairs.size();
}
private void sortPairs()
{
Collections.sort(fPairs, new Comparator<VirtualFileSyncPair>()
{
public int compare(VirtualFileSyncPair pair1, VirtualFileSyncPair pair2)
{
try
{
if (pair1 != null && pair2 != null)
{
if (fDeleteLocal && pair1.getSyncState() == SyncState.ClientItemOnly
&& pair2.getSyncState() == SyncState.ClientItemOnly && pair1.getSourceFile() != null
&& pair2.getSourceFile() != null)
{
String s1 = EFSUtils.getAbsolutePath(pair1.getSourceFile());
String s2 = EFSUtils.getAbsolutePath(pair2.getSourceFile());
if (s1 != null)
{
return -1 * s1.compareToIgnoreCase(s2);
}
}
else if (fDeleteRemote && pair1.getSyncState() == SyncState.ServerItemOnly
&& pair2.getSyncState() == SyncState.ServerItemOnly
&& pair1.getDestinationFile() != null && pair2.getDestinationFile() != null)
{
String s1 = EFSUtils.getAbsolutePath(pair1.getDestinationFile());
String s2 = EFSUtils.getAbsolutePath(pair2.getDestinationFile());
if (s1 != null)
{
return -1 * s1.compareToIgnoreCase(s2);
}
}
}
}
catch (Exception e)
{
// Do nothing and move on
}
return 0;
}
});
}
}