/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.pullrequest.client;
import org.eclipse.che.plugin.pullrequest.client.parts.contribute.ContributePartPresenter;
import org.eclipse.che.plugin.pullrequest.client.vcs.VcsService;
import org.eclipse.che.plugin.pullrequest.client.vcs.VcsServiceProvider;
import org.eclipse.che.plugin.pullrequest.client.vcs.hosting.VcsHostingService;
import org.eclipse.che.plugin.pullrequest.client.vcs.hosting.VcsHostingServiceProvider;
import org.eclipse.che.plugin.pullrequest.client.workflow.Context;
import org.eclipse.che.plugin.pullrequest.client.workflow.WorkflowExecutor;
import com.google.common.base.Optional;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import com.google.web.bindery.event.shared.HandlerRegistration;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.FunctionException;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.promises.client.js.Promises;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.event.SelectionChangedEvent;
import org.eclipse.che.ide.api.event.SelectionChangedHandler;
import org.eclipse.che.ide.api.factory.FactoryAcceptedEvent;
import org.eclipse.che.ide.api.factory.FactoryAcceptedHandler;
import org.eclipse.che.ide.api.parts.PartStack;
import org.eclipse.che.ide.api.parts.WorkspaceAgent;
import org.eclipse.che.ide.api.project.MutableProjectConfig;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.workspace.WorkspaceReadyEvent;
import static org.eclipse.che.plugin.pullrequest.shared.ContributionProjectTypeConstants.CONTRIBUTE_TO_BRANCH_VARIABLE_NAME;
import static org.eclipse.che.plugin.pullrequest.shared.ContributionProjectTypeConstants.CONTRIBUTION_PROJECT_TYPE_ID;
import static java.util.Collections.singletonList;
import static org.eclipse.che.ide.api.constraints.Constraints.FIRST;
import static org.eclipse.che.ide.api.parts.PartStackType.TOOLING;
/**
* Responsible for setting up contribution mixin for the currently selected project in application context.
*
* @author Vlad Zhukovskyi
* @since 5.0.0
*/
@Singleton
public class ContributionMixinProvider {
private final EventBus eventBus;
private final AppContext appContext;
private final WorkspaceAgent workspaceAgent;
private final ContributePartPresenter contributePart;
private final WorkflowExecutor workflowExecutor;
private final VcsServiceProvider vcsServiceProvider;
private final VcsHostingServiceProvider vcsHostingServiceProvider;
private HandlerRegistration handlerRegistration;
private Project lastSelected;
@Inject
public ContributionMixinProvider(EventBus eventBus,
AppContext appContext,
WorkspaceAgent workspaceAgent,
ContributePartPresenter contributePart,
WorkflowExecutor workflowExecutor,
VcsServiceProvider vcsServiceProvider,
VcsHostingServiceProvider vcsHostingServiceProvider) {
this.eventBus = eventBus;
this.appContext = appContext;
this.workspaceAgent = workspaceAgent;
this.contributePart = contributePart;
this.workflowExecutor = workflowExecutor;
this.vcsServiceProvider = vcsServiceProvider;
this.vcsHostingServiceProvider = vcsHostingServiceProvider;
if (appContext.getFactory() != null) {
handlerRegistration = eventBus.addHandler(FactoryAcceptedEvent.TYPE, new FactoryAcceptedHandler() {
@Override
public void onFactoryAccepted(FactoryAcceptedEvent event) {
handlerRegistration.removeHandler();
subscribeToSelectionChangedEvent();
}
});
} else {
handlerRegistration = eventBus.addHandler(WorkspaceReadyEvent.getType(), new WorkspaceReadyEvent.WorkspaceReadyHandler() {
@Override
public void onWorkspaceReady(WorkspaceReadyEvent event) {
handlerRegistration.removeHandler();
subscribeToSelectionChangedEvent();
}
});
}
}
private void subscribeToSelectionChangedEvent() {
eventBus.addHandler(SelectionChangedEvent.TYPE, new SelectionChangedHandler() {
@Override
public void onSelectionChanged(SelectionChangedEvent event) {
processCurrentProject();
}
});
}
private void processCurrentProject() {
final Project rootProject = appContext.getRootProject();
if (lastSelected != null && lastSelected.equals(rootProject)) {
return;
}
final PartStack toolingPartStack = workspaceAgent.getPartStack(TOOLING);
if (rootProject == null) {
if (toolingPartStack.containsPart(contributePart)) {
invalidateContext(lastSelected);
hidePart();
}
} else if (hasVcsService(rootProject)) {
if (hasContributionMixin(rootProject)) {
vcsHostingServiceProvider.getVcsHostingService(rootProject).then(new Operation<VcsHostingService>() {
@Override
public void apply(VcsHostingService vcsHostingService) throws OperationException {
workflowExecutor.init(vcsHostingService, rootProject);
addPart(toolingPartStack);
}
});
} else {
vcsHostingServiceProvider.getVcsHostingService(rootProject)
.then(new Operation<VcsHostingService>() {
@Override
public void apply(final VcsHostingService vcsHostingService)
throws OperationException {
addMixin(rootProject)
.then(new Operation<Project>() {
@Override
public void apply(Project project) throws OperationException {
workflowExecutor.init(vcsHostingService, project);
addPart(toolingPartStack);
lastSelected = project;
}
})
.catchError(new Operation<PromiseError>() {
@Override
public void apply(final PromiseError error) throws OperationException {
invalidateContext(rootProject);
hidePart();
}
});
}
})
.catchError(new Operation<PromiseError>() {
@Override
public void apply(final PromiseError error) throws OperationException {
invalidateContext(rootProject);
hidePart();
}
});
}
} else {
invalidateContext(rootProject);
hidePart();
}
lastSelected = rootProject;
}
private void invalidateContext(Project project) {
final Optional<Context> context = workflowExecutor.getContext(project.getName());
if (context.isPresent()) {
workflowExecutor.invalidateContext(context.get().getProject());
}
}
private void hidePart() {
workspaceAgent.hidePart(contributePart);
workspaceAgent.removePart(contributePart);
}
private void addPart(PartStack partStack) {
if (!partStack.containsPart(contributePart)) {
partStack.addPart(contributePart, FIRST);
}
}
private boolean hasVcsService(Project project) {
return vcsServiceProvider.getVcsService(project) != null;
}
private boolean hasContributionMixin(Project project) {
return project.getMixins().contains(CONTRIBUTION_PROJECT_TYPE_ID);
}
private Promise<Project> addMixin(final Project project) {
final VcsService vcsService = vcsServiceProvider.getVcsService(project);
if (vcsService == null || project.getMixins().contains(CONTRIBUTION_PROJECT_TYPE_ID)) {
return Promises.resolve(project);
}
return vcsService.getBranchName(project)
.thenPromise(new Function<String, Promise<Project>>() {
@Override
public Promise<Project> apply(String branchName) throws FunctionException {
MutableProjectConfig mutableConfig = new MutableProjectConfig(project);
mutableConfig.getMixins().add(CONTRIBUTION_PROJECT_TYPE_ID);
mutableConfig.getAttributes().put(CONTRIBUTE_TO_BRANCH_VARIABLE_NAME,
singletonList(branchName));
return project.update().withBody(mutableConfig).send();
}
});
}
}