Package com.salesforce.ide.ui.sync

Source Code of com.salesforce.ide.ui.sync.SyncController

/*******************************************************************************
* Copyright (c) 2014 Salesforce.com, inc..
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Salesforce.com, inc. - initial API and implementation
******************************************************************************/
package com.salesforce.ide.ui.sync;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.variants.IResourceVariant;

import com.salesforce.ide.core.factories.FactoryException;
import com.salesforce.ide.core.internal.context.ContainerDelegate;
import com.salesforce.ide.core.internal.controller.Controller;
import com.salesforce.ide.core.internal.utils.Constants;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.model.Component;
import com.salesforce.ide.core.model.ComponentList;
import com.salesforce.ide.core.model.OrgModel;
import com.salesforce.ide.core.model.ProjectPackage;
import com.salesforce.ide.core.model.ProjectPackageList;
import com.salesforce.ide.core.project.ForceProjectException;
import com.salesforce.ide.core.remote.ForceConnectionException;
import com.salesforce.ide.core.remote.ForceRemoteException;
import com.salesforce.ide.core.remote.metadata.DeployResultExt;
import com.salesforce.ide.core.remote.metadata.RetrieveResultExt;
import com.salesforce.ide.core.services.ServiceException;
import com.salesforce.ide.core.services.ServiceTimeoutException;

/**
* Handles synchronize functionality.
*
* @author cwall
*/
public class SyncController extends Controller {

    private static final Logger logger = Logger.getLogger(SyncController.class);

    protected ProjectPackageList remoteProjectPackageList = null;
    protected List<IResource> syncResources = null;
    private long fetchRemoteTime = -1;

    //   C O N S T R U C T O R S
    public SyncController(IProject project, boolean filter, IResource... syncResources) throws ForceProjectException {
        super();
        model = new OrgModel(project);
        if (syncResources != null) {
            List<IResource> list = new ArrayList<IResource>();
            for (IResource resource: syncResources) {
              list.add(resource);
            }
           
            setSyncResources(list, filter);
        }     
    }
   
    public SyncController(IProject project, IResource... syncResources) throws ForceProjectException {
      this(project, false, syncResources);
    }
   
    public SyncController(IProject project, List<IResource> syncResources) throws ForceProjectException {
      this(project, syncResources.toArray(new IResource[0]));
    }

    public SyncController(IProject project, List<IResource> syncResources, boolean filter) throws ForceProjectException {
      this(project, filter, syncResources.toArray(new IResource[0]));
    }

    //   M E T H O D S
    public void setSyncResources(List<IResource> syncResources, boolean filter) {
        if (filter) {
            try {
                this.syncResources = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getAllFilesOnly(syncResources);
            } catch (CoreException e) {
                String logMessage = Utils.generateCoreExceptionLog(e);
                logger.warn("Unable to get files from given resources: " + logMessage, e);
                this.syncResources = syncResources;
            }
        } else {
            this.syncResources = syncResources;
        }
    }

    public List<IResource> getSyncResources() {
        return syncResources;
    }

    public ProjectPackageList getRemoteProjectPackageList() {
        return remoteProjectPackageList;
    }

    public void setProjectPackageList(ProjectPackageList projectPackageList) {
        this.remoteProjectPackageList = projectPackageList;
    }

    public ComponentVariantComparator getResourceComparator() {
        return getComponentVariantComparatorInstance();
    }

    public ComponentVariantComparator getComponentVariantComparatorInstance() {
        return new ComponentVariantComparator(this);
    }

    public void loadRemoteComponents(IProgressMonitor monitor) throws InterruptedException, ForceConnectionException,
            FactoryException, CoreException, IOException, ForceRemoteException, InvocationTargetException,
            ServiceException {
        if (getProject() == null) {
            throw new IllegalArgumentException("Resources and/or project cannot be null");
        }

        if (logger.isDebugEnabled()) {
            logger
                    .debug("Getting remote components for selected resources in project '" + getProject().getName()
                            + "'");
        }

        // initial store for remote components
        remoteProjectPackageList = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getProjectPackageListInstance();
        remoteProjectPackageList.setProject(getProject());
        fetchRemoteTime = Calendar.getInstance().getTimeInMillis();

        if (Utils.isEmpty(syncResources)) {
            if (logger.isInfoEnabled()) {
                logger.info("No remote components to fetch - sync resources provided");
            }
            return;
        }

        if (monitor == null) {
            monitor = new NullProgressMonitor();
        }

        monitorCheck(monitor);
        monitorSubTask(monitor, "Retrieving remote components...");

        // handle if source root was selected
        handleSourceRetrieve(monitor);

        // source root covers everything
        if (syncResources.size() == 1 && Utils.isNotEmpty(getFolder(syncResources, Constants.SOURCE_FOLDER_NAME))) {
            return;
        }

        // handle component folders
        handleComponentFolderRefresh(monitor);

        // handle component files
        handleSourceComponentFileRefresh(monitor);
    }

    protected void handleSourceRetrieve(IProgressMonitor monitor) throws InterruptedException,
            ForceConnectionException, ForceRemoteException, FactoryException, CoreException, IOException,
            ServiceException {
        monitorCheck(monitor);
        if (Utils.isNotEmpty(getFolder(syncResources, Constants.SOURCE_FOLDER_NAME))) {
            String packageName = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getPackageName(getProject());

            // perform retrieve
            RetrieveResultExt retrieveResultHandler =
                    ContainerDelegate.getInstance().getServiceLocator().getPackageRetrieveService().retrievePackage(getProject(), packageName, monitor);

            if (retrieveResultHandler == null) {
                logger.warn("Unable to sync source root - retrieve result is null");
                return;
            }

            monitorWork(monitor);
            remoteProjectPackageList.generateComponents(retrieveResultHandler.getZipFile(), retrieveResultHandler
                    .getFileMetadataHandler(), new SubProgressMonitor(monitor, 3));
        }
    }

    protected void handleComponentFolderRefresh(IProgressMonitor monitor) throws InterruptedException,
            ForceConnectionException, ForceRemoteException, FactoryException, CoreException, IOException,
            InvocationTargetException, ServiceException {
        monitorCheck(monitor);
        List<IResource> folders = getResourcesByType(syncResources, IResource.FOLDER);
        List<String> componentTypes = new ArrayList<String>();
        if (Utils.isNotEmpty(folders)) {
            for (IResource folder : folders) {
                if (ContainerDelegate.getInstance().getServiceLocator().getProjectService().isComponentFolder(folder)) {
                    Component component = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getComponentByFolderName(folder.getName());
                    componentTypes.add(component.getComponentType());
                } else if (ContainerDelegate.getInstance().getServiceLocator().getProjectService().isSubComponentFolder(folder)) {
                    Component component = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getComponentFromSubFolder((IFolder) folder, false);
                    componentTypes.add(component.getSecondaryComponentType());
                }
            }

            if (Utils.isNotEmpty(componentTypes)) {
                // refresh component dirs
                ProjectPackageList localProjectPackageList =
                        ContainerDelegate.getInstance().getServiceLocator().getProjectService().getProjectPackageFactory().getProjectPackageListInstance(getProject());

                // only save these types
                String[] saveComponentTypes = componentTypes.toArray(new String[componentTypes.size()]);

                // perform retrieve
                RetrieveResultExt retrieveResultHandler =
                        ContainerDelegate.getInstance().getServiceLocator().getPackageRetrieveService().retrieveSelective(localProjectPackageList,
                            saveComponentTypes, monitor);

                remoteProjectPackageList.generateComponents(retrieveResultHandler.getZipFile(), retrieveResultHandler
                        .getFileMetadataHandler(), new SubProgressMonitor(monitor, 3));
            }
        }
    }

    protected void handleSourceComponentFileRefresh(IProgressMonitor monitor) throws InterruptedException,
            ForceConnectionException, ForceRemoteException, FactoryException, CoreException, IOException,
            InvocationTargetException, ServiceException {
        monitorCheck(monitor);
        List<IResource> files = getResourcesByType(syncResources, IResource.FILE);
        if (Utils.isNotEmpty(files)) {
            List<IResource> sourceResources = new ArrayList<IResource>(files.size());
            for (IResource file : files) {
                if (ContainerDelegate.getInstance().getServiceLocator().getProjectService().isSourceResource(file)) {
                    sourceResources.add(file);
                }
            }

            ProjectPackageList localProjectPackageList =
                    ContainerDelegate.getInstance().getServiceLocator().getProjectService().getProjectContents(sourceResources, monitor);
            localProjectPackageList.setProject(getProject());
            monitorCheck(monitor);
            RetrieveResultExt retrieveResultHandler =
                    ContainerDelegate.getInstance().getServiceLocator().getPackageRetrieveService()
                            .retrieveSelective(localProjectPackageList, true, monitor);

            remoteProjectPackageList.generateComponents(retrieveResultHandler.getZipFile(), retrieveResultHandler
                    .getFileMetadataHandler(), new SubProgressMonitor(monitor, 3));
        }
    }

    /**
     * Assemble variant data to be compared for local and remote differences.
     *
     * @param resource
     * @return
     * @throws FactoryException
     * @throws TeamException
     */
    public SyncInfo getSyncInfo(IResource resource) throws TeamException {
        if (!ContainerDelegate.getInstance().getServiceLocator().getProjectService().isManagedFile(resource)) {
            return null;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Get sync info for '" + resource.getProjectRelativePath().toPortableString() + "'");
        }

        ComponentVariant baseVariant = null;
        IFile file = (IFile) resource;

        if (file == null) {
            logger.warn("File is null. Skipping as sync candidate");
            return null;
        }

        Component baseComponent = null;
        try {
            baseComponent = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getComponentFromFile(file, true);
        } catch (FactoryException e) {
            logger.error("Unable to get component from file '" + file.getProjectRelativePath().toPortableString()
                    + "'. Skipping as sync candidate");
            return null;
        }

        // exclude default manifest
        if (baseComponent == null
                || (baseComponent.isPackageManifest() && Constants.DEFAULT_PACKAGED_NAME.equals(baseComponent
                        .getPackageName()))) {
            if (logger.isInfoEnabled()) {
                logger.info("Base component is the default package manifest - skipping ");
            }
            return null;
        }

        // prepare remote variant.
        Component remoteComponent = null;
        if (file.exists()) {
            // we only have a base variant if the file exist locally and we assemble a corresponding base component
            baseVariant = new ComponentVariant(baseComponent);

            // get remote component from pre-fetched remote project package list
            remoteComponent = getComponentFromRemoteList(baseComponent);

            // if we cannot find component in remote list, then we'll assume component was
            // created locally and not yet saved to remote server
            if (remoteComponent != null) {
                remoteComponent.setFileResource(baseComponent.getFileResource());
                remoteComponent.setPackageName(baseComponent.getPackageName());
                remoteComponent.setInstalled(baseComponent.isInstalled());
            }
        } else if (!file.exists() && Utils.isNotEmpty(file.getName())) {
            // typically this means file exists remotely, so there will only be a remote variant
            remoteComponent = getRemoteComponentByFilePath(file);
        } else {
            logger.warn("File '" + file.getName()
                    + "' does not exist and remote component not found. Skipping as sync candidate");
            return null;
        }

        ComponentVariant remoteVariant = null;
        if (remoteComponent != null) {
            remoteVariant = new ComponentVariant(remoteComponent);
            remoteVariant.setRemote(true);
        } else {
            if (logger.isInfoEnabled()) {
                logger
                        .info("Remote component not found.  Assuming component was created locally and not saved remotely");
            }
            // REVIEWME: temp workaround to trick compare algorithm that change is a new outgoing addition
            baseVariant = null;
        }

        SyncInfo syncInfo =
                new ComponentSyncInfo(resource, baseVariant, remoteVariant, getComponentVariantComparatorInstance());
        syncInfo.init();

        // if in sync, remove resource for further consideration
        if (syncInfo.getKind() == SyncInfo.IN_SYNC) {
            if (logger.isDebugEnabled()) {
                logger.debug("Resource '" + resource.getProjectRelativePath().toPortableString()
                        + "' is in sync - removing as a cached sync resource");
            }
            syncResources.remove(resource);
        }

        return syncInfo;
    }

    /**
     * Aggregates resources contained in a given resource.
     *
     * @param resource
     * @return
     * @throws CoreException
     */
    public IResource[] members(IResource resource) throws CoreException {
        if (logger.isDebugEnabled()) {
            logger.debug("Assembling list of members of '" + resource.getName() + "' resource");
        }
        // files do not have members
        if (resource.getType() == IResource.FILE) {
            return getResources((IFile) resource);
        } else if (resource.getType() == IResource.PROJECT) {
            // projects can have members and we are only interested in managed folders
            return getResources((IProject) resource);
        } else if (resource.getType() != IResource.FOLDER) {
            // if you are neither a project nor a file, you must be a folder
            return new IResource[0];
        } else if (resource.getType() == IResource.FOLDER) {
            return getResources((IFolder) resource);
        } else {
            return new IResource[0];
        }
    }

    // sync api calls this to determine the root from which project resources will be gathered for sync inspection
    public IResource[] roots() {
        if (Utils.isEmpty(syncResources)) {
            if (logger.isDebugEnabled()) {
                logger.debug("No sync resource found");
            }
            return new IResource[0];
        }

        if (logger.isDebugEnabled()) {
            StringBuffer strBuff = new StringBuffer("Gathered [" + syncResources.size() + "] sync roots");
            int resourceCnt = 0;
            synchronized (syncResources) {
                for (IResource resource : syncResources) {
                    strBuff.append("\n (").append(++resourceCnt).append(") ").append(
                        resource.getProjectRelativePath().toPortableString());
                }
            }

            logger.debug(strBuff.toString());
        }

        return syncResources.toArray(new IResource[syncResources.size()]);
    }

    public boolean isSupervised(IResource resource) throws TeamException {
        return ContainerDelegate.getInstance().getServiceLocator().getProjectService().isManagedFile(resource);
    }

    private IResource[] getResources(IProject project) throws CoreException {
        Set<IResource> syncCandidates = new HashSet<IResource>();
        IResource[] childResources = getProject().members();
        if (Utils.isNotEmpty(childResources)) {
            for (IResource childResource : childResources) {
                if (ContainerDelegate.getInstance().getServiceLocator().getProjectService().isSourceFolder(childResource)) {
                    boolean exits = syncCandidates.add(childResource);
                    if (exits) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Added resource '" + childResource.getName() + "' as a local sync candidate");
                        }
                    } else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Resource '" + childResource.getName() + "' already exists as sync candidate");
                        }
                    }
                }
            }
        }

        logResources(syncCandidates);

        return syncCandidates.toArray(new IResource[syncCandidates.size()]);
    }

    private IResource[] getResources(IFolder folder) throws CoreException {
        // we're only interested in files that pertain to given org - not referenced packages
        if (!ContainerDelegate.getInstance().getServiceLocator().getProjectService().isManagedFolder(folder)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping folder '" + folder.getName() + "' as managed resource");
            }
            return new IResource[0];
        }

        Set<IResource> syncCandidates = new HashSet<IResource>();
        List<IResource> members = null;
        members = new ArrayList<IResource>(Arrays.asList(folder.members()));

        for (IResource resource : members) {
            if (resource.getType() == IResource.FOLDER) {
                Set<IResource> subFolderMembers = getFolderResources(syncCandidates, (IFolder) resource);
                syncCandidates.addAll(subFolderMembers);
            } else if (resource.getType() == IResource.FILE) {
                addFileResource(syncCandidates, (IFile) resource);
            } else {
                logger.warn("Resource '" + resource.getName() + "' is not considered for sync'ing");
            }
        }

        addRemoteSyncCandidates(syncCandidates, remoteProjectPackageList);

        logResources(syncCandidates);

        return syncCandidates.toArray(new IResource[syncCandidates.size()]);
    }

    private IResource[] getResources(IFile file) throws CoreException {
        // we're only interested in files that pertain to given org - not referenced packages
        if (!ContainerDelegate.getInstance().getServiceLocator().getProjectService().isManagedResource(file)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping folder '" + file.getName() + "' as managed resource");
            }
            return new IResource[0];
        }

        Set<IResource> syncCandidates = new HashSet<IResource>();
        addFileResource(syncCandidates, file);

        addRemoteSyncCandidates(syncCandidates, remoteProjectPackageList);

        logResources(syncCandidates);

        return syncCandidates.toArray(new IResource[syncCandidates.size()]);
    }

    private Set<IResource> getFolderResources(Set<IResource> syncCandidates, IFolder folder) throws CoreException {
        // for managed folders, consider managed files locally
        List<IResource> members = null;
        members = new ArrayList<IResource>(Arrays.asList(folder.members()));

        for (IResource resource : members) {
            if (resource.getType() == IResource.FOLDER) {
                Set<IResource> subFolderMembers = getFolderResources(syncCandidates, (IFolder) resource);
                syncCandidates.addAll(subFolderMembers);
            } else if (resource.getType() == IResource.FILE) {
                addFileResource(syncCandidates, (IFile) resource);
            } else {
                logger.warn("Resource '" + resource.getName() + "' is not considered for sync'ing");
            }
        }

        return syncCandidates;
    }

    // add remote components to sync candidate list.
    // this captures components that were created remotely and do not exist w/in the existing project
    private void addRemoteSyncCandidates(Set<IResource> syncCandidates, ProjectPackageList projectPackageList) {
        if (syncCandidates == null || Utils.isEmpty(projectPackageList)) {
            logger.warn("No remote resources to add - sync candiates null or project package list null/empty");
            return;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Adding remote components to sync candidate list");
        }
        for (ProjectPackage projectPackage : projectPackageList) {
            ComponentList componentList = projectPackage.getComponentList();
            if (Utils.isNotEmpty(componentList)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Found [" + componentList.size() + "] components in package '"
                            + projectPackage.getName() + "' to evaluate");
                }
                for (Component component : componentList) {
                    IResource resource = component.getFileResource(projectPackageList.getProject());
                    if (resource == null || ContainerDelegate.getInstance().getServiceLocator().getProjectService().isDefaultPackageManifestFile(resource)
                            || !ContainerDelegate.getInstance().getServiceLocator().getProjectService().isManagedFile(resource)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Excluding '" + resource != null ? resource.getName() : component
                                    .getMetadataFilePath()
                                    + "' resource - resource is null and/or resource is not a "
                                    + Constants.PLUGIN_NAME
                                    + " managed resource");
                        }
                        continue;
                    }

                    boolean addSuccess = syncCandidates.add(resource);
                    if (addSuccess) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Added resource '" + resource.getProjectRelativePath().toPortableString()
                                    + "' as a remote sync candidate");
                        }
                    } else {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Resource '" + resource.getProjectRelativePath().toPortableString()
                                    + "' already exists as sync candidate");
                        }
                    }
                }
            }
        }
    }

    private void addFileResource(Set<IResource> syncCandidates, IFile file) {
        // we're only interested in files that pertain to given org - not installed packages & package manifest
        if (syncCandidates == null || file == null || ContainerDelegate.getInstance().getServiceLocator().getProjectService().isDefaultPackageManifestFile(file)
                || !ContainerDelegate.getInstance().getServiceLocator().getProjectService().isManagedFile(file)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping resource '" + file.getName() + "' as managed resource");
            }
            return;
        }

        // add resource as sync candidate
        addResource(syncCandidates, file);
    }

    private void addResource(Set<IResource> syncCandidates, IResource resource) {
        // add resource to candidates
        boolean exits = syncCandidates.add(resource);
        if (exits) {
            if (logger.isDebugEnabled()) {
                logger.debug("Added resource '" + resource.getName() + "' as a local sync candidate");
            }
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("Resource '" + resource.getName() + "' already exists as sync candidate");
            }
        }
    }

    private Component getComponentFromRemoteList(Component localComponent) {
        if (Utils.isEmpty(remoteProjectPackageList) || localComponent == null) {
            return null;
        }

        Component remoteComponent =
                remoteProjectPackageList.getComponentByFilePath(localComponent.getMetadataFilePath());

        // REVIEWME: re-think impl; maybe refetch just out-of-date component
        // since we cache remote components, remote components become out-of-date.
        if ((remoteComponent != null && !remoteComponent.isFetchedAfter(localComponent.getFetchTime()))
                || localComponent.isFetchedAfter(fetchRemoteTime)) {
            if (logger.isInfoEnabled()) {
                logger.info("Remote component " + (remoteComponent != null ? remoteComponent.getFullDisplayName() : "")
                        + " is out-of-date - refreshing all remote components");
            }
            try {
                loadRemoteComponents(null);
            } catch (Exception e) {
                logger.error("Unable to refetch remote compoents", e);
            }
        }
        return remoteComponent;
    }

    private Component getRemoteComponentByFilePath(IFile file) {
        Component component = null;
        if (Utils.isNotEmpty(remoteProjectPackageList) && file != null) {
            String filePath = file.getProjectRelativePath().toPortableString();
            component = remoteProjectPackageList.getComponentByFilePath(filePath);
        }
        return component;
    }

    //  S Y N C   O P E R A T I O N S
    public boolean applyToProject(ComponentSubscriber subscriber, SyncInfo[] syncInfos, IProgressMonitor monitor)
            throws InterruptedException, CoreException, InvocationTargetException, IOException, ForceProjectException,
            Exception {
        if (Utils.isEmpty(syncInfos)) {
            throw new IllegalArgumentException("Sync array and/or subscriber cannot be null");
        }

        boolean result = true;
        List<SyncInfo> projectDeletes = new ArrayList<SyncInfo>();
        List<SyncInfo> projectSaves = new ArrayList<SyncInfo>();
        for (SyncInfo syncInfo : syncInfos) {
            int change = SyncInfo.getChange(syncInfo.getKind());
            // outgoing
            if (SyncInfo.getDirection(syncInfo.getKind()) == SyncInfo.OUTGOING) {
                if (change == SyncInfo.DELETION || change == SyncInfo.CHANGE) {
                    projectSaves.add(syncInfo);
                } else if (change == SyncInfo.ADDITION) {
                    projectDeletes.add(syncInfo);
                }
            } else {
                // incoming
                if (change == SyncInfo.ADDITION || change == SyncInfo.CHANGE) {
                    projectSaves.add(syncInfo);
                } else if (change == SyncInfo.DELETION) {
                    projectDeletes.add(syncInfo);
                }
            }
        }

        if (Utils.isNotEmpty(projectDeletes)) {
            result = deleteFromProject(projectDeletes, monitor);
        }

        if (result && Utils.isNotEmpty(projectSaves)) {
            result = saveToProject(projectSaves, monitor);
        }

        if (result && subscriber != null) {
            // refresh sync view
            subscriber.refresh(new SubProgressMonitor(monitor, 5));
        }

        return result;
    }

    /**
     * Apply selected remote synchronize conflict to local project.
     *
     * @param syncInfos
     * @return
     * @throws InvocationTargetException
     * @throws InterruptedException
     * @throws IOException
     * @throws CoreException
     * @throws ForceProjectException
     */
    protected boolean saveToProject(final List<SyncInfo> syncInfos, IProgressMonitor monitor)
            throws InvocationTargetException, InterruptedException, CoreException, IOException, ForceProjectException,
            Exception {
        if (Utils.isEmpty(syncInfos)) {
            throw new IllegalArgumentException("Sync array and/or subscriber cannot be null");
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Apply server change to local");
        }

        try {
            ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
                public void run(IProgressMonitor monitor) throws CoreException {
                    try {
                        for (SyncInfo syncInfo : syncInfos) {
                            monitorCheck(monitor);
                            ComponentVariant componentVariant = (ComponentVariant) syncInfo.getRemote();
                            if (componentVariant != null) {
                                Component remoteComponent = componentVariant.getComponent();

                                // skip auto build for this operation
                                ContainerDelegate.getInstance().getServiceLocator().getProjectService().flagSkipBuilder(getProject());

                                monitorCheck(monitor);
                                syncResources.remove(syncInfo.getLocal());
                                IFile file = remoteComponent.saveToFile(true, monitor);

                                if (logger.isInfoEnabled()) {
                                    logger.info("Saved '" + file.getProjectRelativePath().toPortableString()
                                            + "' to project '" + file.getProject().getName()
                                            + "' and remove as sync resource");
                                }
                            }
                        }
                    } catch (Exception e) {
                        throw new CoreException(new Status(IStatus.ERROR, Constants.FORCE_PLUGIN_PREFIX, 0, e
                                .getMessage(), e));
                    }
                }
            }, null, IResource.NONE, monitor);
        } catch (CoreException e) {
            throw (Exception) e.getCause();
        }

        return true;
    }

    public boolean deleteFromProject(final List<SyncInfo> syncInfos, IProgressMonitor monitor)
            throws InterruptedException, CoreException, InvocationTargetException, Exception {
        if (Utils.isEmpty(syncInfos)) {
            throw new IllegalArgumentException("SyncInfo array cannot be null");
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Delete from project");
        }

        class Result {
            boolean result = true;
        }

        final Result result = new Result();

        try {
            ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
                public void run(IProgressMonitor monitor) throws CoreException {
                    try {
                        for (SyncInfo syncInfo : syncInfos) {
                            monitorCheck(monitor);
                            IResource resource = syncInfo.getLocal();
                            if (resource == null || !resource.exists() || resource.getType() != IResource.FILE) {
                                logger
                                        .error("Unable to delete resource from project - resource is null or does not exist or is not a file");
                                result.result = false;
                                break;
                            }

                            // skip auto build for this operation
                            ContainerDelegate.getInstance().getServiceLocator().getProjectService().flagSkipBuilder(getProject());

                            handleCompositeResourceDelete((IFile) resource, monitor);
                            deleteResource(resource, monitor);
                        }
                    } catch (Exception e) {
                        throw new CoreException(new Status(IStatus.ERROR, Constants.FORCE_PLUGIN_PREFIX, 0, e
                                .getMessage(), e));
                    }
                }
            }, null, IResource.NONE, monitor);
        } catch (CoreException e) {
            throw (Exception) e.getCause();
        }

        return result.result;
    }

    protected boolean handleCompositeResourceDelete(IFile file, IProgressMonitor monitor) throws InterruptedException,
            CoreException {
        IFile compositeFile = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getCompositeFileResource(file);
        if (compositeFile != null) {
            deleteResource(compositeFile, monitor);
        }
        return true;
    }

    protected void deleteResource(IResource resource, IProgressMonitor monitor) throws InterruptedException,
            CoreException {
        String resourceFilePath = resource.getProjectRelativePath().toPortableString();
        String projectName = resource.getProject().getName();

        monitorCheck(monitor);
        syncResources.remove(resource);
        resource.delete(true, monitor);

        if (logger.isInfoEnabled()) {
            logger.info("Delete '" + resourceFilePath + "' from project '" + projectName
                    + "' and remove as sync resource");
        }
    }

    /**
     * Apply selected local synchronize conflict to server.
     *
     * @param subscriber
     * @param infos
     * @return
     * @throws SyncException
     * @throws InterruptedException
     * @throws InterruptedException
     * @throws IOException
     * @throws CoreException
     * @throws ServiceException
     * @throws FactoryException
     * @throws ForceRemoteException
     * @throws ForceConnectionException
     * @throws InvocationTargetException
     */
    public boolean applyToServer(ComponentSubscriber subscriber, SyncInfo[] syncInfos, IProgressMonitor monitor)
            throws SyncException, InterruptedException, ForceConnectionException, ForceRemoteException,
            FactoryException, ServiceException, CoreException, IOException, InvocationTargetException, Exception {
        if (Utils.isEmpty(syncInfos)) {
            throw new IllegalArgumentException("Sync array and/or subscriber cannot be null");
        }

        boolean result = true;
        List<SyncInfo> serverDeletes = new ArrayList<SyncInfo>();
        List<SyncInfo> serverSaves = new ArrayList<SyncInfo>();
        for (SyncInfo syncInfo : syncInfos) {
            int change = SyncInfo.getChange(syncInfo.getKind());
            if (SyncInfo.getDirection(syncInfo.getKind()) == SyncInfo.INCOMING) {
                if (change == SyncInfo.DELETION || change == SyncInfo.CHANGE) {
                    serverSaves.add(syncInfo);
                } else if (change == SyncInfo.ADDITION) {
                    serverDeletes.add(syncInfo);
                }
            } else {
                if (change == SyncInfo.ADDITION || change == SyncInfo.CHANGE) {
                    serverSaves.add(syncInfo);
                } else if (change == SyncInfo.DELETION) {
                    serverDeletes.add(syncInfo);
                }
            }
        }

        if (Utils.isNotEmpty(serverDeletes)) {
            result = deleteFromServer(serverDeletes, monitor);
        }

        if (result && Utils.isNotEmpty(serverSaves)) {
            result = saveToServer(serverSaves, monitor);
        }

        if (result && subscriber != null) {
            // refresh sync candidates
            subscriber.refresh(getResources(syncInfos), new SubProgressMonitor(monitor, 5));
        }

        return result;
    }

    protected boolean saveToServer(List<SyncInfo> syncInfos, IProgressMonitor monitor) throws InterruptedException,
            FactoryException, ForceConnectionException, CoreException, IOException, ServiceException,
            ForceRemoteException, InvocationTargetException, Exception {
        if (logger.isDebugEnabled()) {
            logger.debug("Apply local change to server");
        }

        monitorCheck(monitor);
        ProjectPackageList projectPackageList = generateProjectPackageList(syncInfos, monitor);

        monitorCheck(monitor);
        DeployResultExt deployResultHandler = ContainerDelegate.getInstance().getServiceLocator().getPackageDeployService().deploy(projectPackageList, monitor);
        boolean result = ContainerDelegate.getInstance().getServiceLocator().getProjectService().handleDeployResult(projectPackageList, deployResultHandler, true, monitor);

        if (result) {
            if (logger.isInfoEnabled()) {
                logger.info("Saved the following components from server:\n"
                        + projectPackageList.getAllComponents().toStringLite());
            }

            for (SyncInfo syncInfo : syncInfos) {
                IResource resource = syncInfo.getLocal();
                if (logger.isInfoEnabled()) {
                    logger.info("Remove '" + resource.getProjectRelativePath().toPortableString()
                            + "' as sync resource");
                }
                syncResources.remove(resource);
            }
        } else {
            logger.warn("Failed to apply saves to server");
        }

        return true;
    }

    public boolean deleteFromServer(List<SyncInfo> syncInfos, IProgressMonitor monitor) throws FactoryException,
            InterruptedException, ForceConnectionException, ServiceException, CoreException, IOException,
            ForceRemoteException, InvocationTargetException, Exception {
        if (Utils.isEmpty(syncInfos)) {
            throw new IllegalArgumentException("SyncInfo array cannot be null");
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Apply local delete to server");
        }

        ProjectPackageList projectPackageList = generateProjectPackageList(syncInfos, monitor);
        // ensure that each project package has a delete manifest
        for (ProjectPackage projectPackage : projectPackageList) {
            ContainerDelegate.getInstance().getServiceLocator().getProjectService().getPackageManifestFactory().attachDeleteManifest(projectPackage);
        }

        monitorCheck(monitor);
        DeployResultExt deployResultHandler = null;
        try {
            deployResultHandler = ContainerDelegate.getInstance().getServiceLocator().getPackageDeployService().deployDelete(projectPackageList, false, monitor);
        } catch (ServiceTimeoutException ex) {
            deployResultHandler =
                    ContainerDelegate.getInstance().getServiceLocator().getPackageDeployService().handleDeployServiceTimeoutException(ex,
                        "delete from server", monitor);
        }

        monitorCheck(monitor);
        boolean result = ContainerDelegate.getInstance().getServiceLocator().getProjectService().handleDeployResult(projectPackageList, deployResultHandler, true, monitor);

        if (result) {
            if (logger.isInfoEnabled()) {
                logger.info("Delete the following components from server:\n"
                        + projectPackageList.getAllComponents().toStringLite());
            }

            for (SyncInfo syncInfo : syncInfos) {
                IResource resource = syncInfo.getLocal();
                if (logger.isInfoEnabled()) {
                    logger.info("Remove '" + resource.getProjectRelativePath().toPortableString()
                            + "' as sync resource");
                }
                syncResources.remove(resource);
            }
        } else {
            logger.warn("Failed to apply deletes to server");
        }

        return true;
    }

    protected IResource[] getResources(SyncInfo[] syncInfos) {
        if (Utils.isEmpty(syncInfos)) {
            return null;
        }

        List<IResource> resources = new ArrayList<IResource>();
        for (SyncInfo syncInfo : syncInfos) {
            resources.add(syncInfo.getLocal());
        }

        return resources.toArray(new IResource[resources.size()]);

    }

    protected ProjectPackageList generateProjectPackageList(List<SyncInfo> syncInfos, IProgressMonitor monitor)
            throws FactoryException, InterruptedException {

        ProjectPackageList projectPackageList = ContainerDelegate.getInstance().getServiceLocator().getProjectService().getProjectPackageListInstance();
        if (Utils.isEmpty(syncInfos)) {
            return projectPackageList;
        }

        for (SyncInfo syncInfo : syncInfos) {
            monitorCheck(monitor);
            IResource res = syncInfo.getLocal();
            if (res.getType() == IResource.FILE) {
                IFile file = (IFile) res;
                projectPackageList.setProject(file.getProject());

                monitorCheck(monitor);
                Component component = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getComponentFromFile(file, true);
                projectPackageList.addComponent(component, true);
            }
        }
        return projectPackageList;
    }

    //   C O M P A R A T O R   O P E R A T I O N S
    public boolean compare(IResource file, IResourceVariant baseVariant) {
        if (file == null || baseVariant == null) {
            throw new IllegalArgumentException("Resource and/or variant cannot be null");
        }

        boolean changed = false;

        if (file.getType() != IResource.FILE || !(baseVariant instanceof ComponentVariant)) {
            logger.warn("Unable to compare local file and variant");
            return changed;
        }

        IFile componentFile = (IFile) file;
        ComponentVariant baseComponentVariant = (ComponentVariant) baseVariant;
        Component baseComponent = baseComponentVariant.getComponent();

        if (baseComponent == null) {
            logger.warn("Unable to compare file and variant - base component is null");
            return changed;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Comparing local file '" + componentFile.getName() + "' with base component "
                    + baseComponent.getFullDisplayName());
        }

        Component localComponent = null;
        try {
            localComponent = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory().getComponentFromFile(componentFile, true);
        } catch (FactoryException e) {
            logger.error("Unable to load component from file '" + componentFile.getName() + "'", e);
            // REVIEWME: is assuming no change the best way to handle this scenario?
            return changed;
        }

        if (localComponent != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Is base variant a remote component? " + baseComponentVariant.isRemote());
            }
            changed = localComponent.hasLocalChanged();
        } else {
            logger.warn("Local component is null.");
        }

        if (logger.isInfoEnabled()) {
            logger.info("Local " + localComponent.getFullDisplayName() + " body is " + (changed ? "OUT" : "IN")
                    + " of sync with original body.");
        }

        // if changed, then we return that file and variant are not equal
        return !changed;
    }

    public boolean compare(IResourceVariant baseVariant, IResourceVariant remoteVariant) {
        if (remoteVariant == null || baseVariant == null) {
            throw new IllegalArgumentException("Base and/or remote variant cannot be null");
        }

        boolean changed = false;
        if (!(baseVariant instanceof ComponentVariant)
                || !(remoteVariant instanceof ComponentVariant)) {
            logger.warn("Unable to compare base and remote variant");
            return changed;
        }

        ComponentVariant baseComponentVariant = (ComponentVariant) baseVariant;
        Component baseComponent = baseComponentVariant.getComponent();

        ComponentVariant remoteComponentVariant = (ComponentVariant) remoteVariant;
        Component remoteComponent = remoteComponentVariant.getComponent();

        if (remoteComponent == null || baseComponent == null || isDefaultPackageManifest(remoteComponent)
                || isDefaultPackageManifest(baseComponent)) {
            logger.warn("Unable to compare base and remote variant - base and/or remote component is null "
                    + "or is default manifest");
            return changed;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Comparing base and remote " + baseComponent.getFullDisplayName());
        }

        try {
            changed = baseComponent.hasRemoteChanged(remoteComponent, false, new NullProgressMonitor());
        } catch (InterruptedException e) {
            // do nothing - thrown if user cancels
        }

        if (logger.isInfoEnabled()) {
            logger.info("Remote " + remoteComponent.getFullDisplayName() + " body is " + (changed ? "OUT" : "IN")
                    + " of sync with local original body.");
        }

        // if changed, then we return that base and remote variant are not equal
        return !changed;
    }

    private boolean isDefaultPackageManifest(Component component) {
        return (component.isPackageManifest() && Constants.DEFAULT_PACKAGED_NAME.equals(component.getName()));
    }

    private void logResources(Set<IResource> forceFolders) {
        if (logger.isDebugEnabled() && Utils.isNotEmpty(forceFolders)) {
            StringBuffer strBuff = new StringBuffer();
            strBuff.append("Assembled the following resources ").append("[").append(forceFolders.size()).append(
                "] to be inspected: ");
            int resCnt = 0;
            for (IResource resource : forceFolders) {
                strBuff.append("\n (").append(++resCnt).append(") ").append(
                    resource.getProjectRelativePath().toPortableString()).append(", ").append(" type=").append(
                    resource.getType());
            }
            logger.debug("\n" + strBuff.toString());
        }
    }

    @Override
    public void finish(IProgressMonitor monitor) throws Exception {

    }

    @Override
    public void init() {

    }

    public void clean() {
        if (Utils.isEmpty(syncResources)) {
            return;
        }
        syncResources.clear();
    }

    @Override
    public void dispose() {

    }

    private List<IResource> getResourcesByType(List<IResource> resources, int type) {
        if (Utils.isEmpty(resources)) {
            return null;
        }

        List<IResource> specificResources = new ArrayList<IResource>(resources.size());
        for (IResource resource : resources) {
            if (resource.getType() == type) {
                specificResources.add(resource);
            }
        }
        return specificResources;
    }

    private IResource getFolder(List<IResource> resources, String name) {
        if (Utils.isEmpty(resources)) {
            return null;
        }

        for (IResource resource : resources) {
            if (resource.getType() == IResource.FOLDER && resource.getName().endsWith(name)) {
                return resource;
            }
        }

        return null;
    }
}
TOP

Related Classes of com.salesforce.ide.ui.sync.SyncController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.