/*******************************************************************************
* Copyright (c) 2012-2016 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.ide.extension.machine.client.perspective.widgets.recipe;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.machine.gwt.client.RecipeServiceClient;
import org.eclipse.che.api.machine.shared.dto.recipe.NewRecipe;
import org.eclipse.che.api.machine.shared.dto.recipe.RecipeDescriptor;
import org.eclipse.che.api.machine.shared.dto.recipe.RecipeUpdate;
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.ide.api.event.ActivePartChangedEvent;
import org.eclipse.che.ide.api.event.ActivePartChangedHandler;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.parts.base.BasePresenter;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.extension.machine.client.MachineLocalizationConstant;
import org.eclipse.che.ide.extension.machine.client.MachineResources;
import org.eclipse.che.ide.extension.machine.client.perspective.widgets.recipe.container.RecipesContainerPresenter;
import org.eclipse.che.ide.extension.machine.client.perspective.widgets.recipe.editor.RecipeEditorPanel;
import org.eclipse.che.ide.extension.machine.client.perspective.widgets.recipe.editor.RecipeEditorView;
import org.eclipse.che.ide.extension.machine.client.perspective.widgets.recipe.entry.RecipeEntry;
import org.eclipse.che.ide.extension.machine.client.perspective.widgets.recipe.entry.RecipeWidget;
import org.eclipse.che.ide.extension.machine.client.util.NameGenerator;
import org.vectomatic.dom.svg.ui.SVGResource;
import javax.validation.constraints.NotNull;
import org.eclipse.che.commons.annotation.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
/**
* The class defines methods which contains business logic to control recipe's script.
*
* @author Valeriy Svydenko
*/
@Singleton
public class RecipePartPresenter extends BasePresenter implements RecipePartView.ActionDelegate,
RecipeEntry.ActionDelegate,
RecipeEditorPanel.ActionDelegate,
ActivePartChangedHandler {
private static final String RECIPE_TYPE = "docker";
private final RecipePartView view;
private final MachineLocalizationConstant locale;
private final MachineResources resources;
private final RecipesContainerPresenter recipesContainerPresenter;
private final NotificationManager notificationManager;
private final RecipeServiceClient service;
private final DtoFactory dtoFactory;
private RecipeWidget selectedRecipe;
private HashMap<RecipeWidget, RecipeDescriptor> recipes;
@Inject
public RecipePartPresenter(RecipePartView view,
MachineResources resources,
EventBus eventBus,
NotificationManager notificationManager,
MachineLocalizationConstant locale,
RecipesContainerPresenter recipesContainerPresenter,
DtoFactory dtoFactory,
RecipeServiceClient service) {
this.view = view;
this.locale = locale;
this.notificationManager = notificationManager;
this.resources = resources;
this.recipesContainerPresenter = recipesContainerPresenter;
this.service = service;
this.dtoFactory = dtoFactory;
recipes = new HashMap<>();
view.setDelegate(this);
eventBus.addHandler(ActivePartChangedEvent.TYPE, this);
}
/** Shows all created recipes. */
public void showRecipes() {
Promise<List<RecipeDescriptor>> recipesPromise = service.getAllRecipes();
recipesPromise.then(new Operation<List<RecipeDescriptor>>() {
@Override
public void apply(List<RecipeDescriptor> recipeDescriptors) throws OperationException {
view.clear();
for (RecipeDescriptor recipeDescriptor : recipeDescriptors) {
addRecipe(recipeDescriptor);
}
if (recipes.isEmpty()) {
showEditorStubPanel();
} else {
selectRecipe();
}
}
});
}
/**
* Adds new recipe to list.
*
* @param recipeDescriptor
* a descriptor of new recipe
*/
@NotNull
private RecipeWidget addRecipe(@NotNull RecipeDescriptor recipeDescriptor) {
RecipeWidget recipe = new RecipeWidget(recipeDescriptor, resources);
recipe.setDelegate(this);
view.addRecipe(recipe);
recipesContainerPresenter.addRecipePanel(recipe);
recipes.put(recipe, recipeDescriptor);
return recipe;
}
/** {@inheritDoc} */
@Override
public void onDeleteButtonClicked() {
final RecipeDescriptor selectedRecipeDescriptor = recipes.get(selectedRecipe);
Promise<Void> recipeRemoved = service.removeRecipe(selectedRecipeDescriptor.getId());
recipeRemoved.then(new Operation<Void>() {
@Override
public void apply(Void arg) throws OperationException {
notificationManager.notify("Recipe \"" + selectedRecipeDescriptor.getName() + "\" was deleted.");
recipes.remove(selectedRecipe);
view.removeRecipe(selectedRecipe);
recipesContainerPresenter.removeRecipePanel(selectedRecipe);
selectedRecipe = null;
if (recipes.isEmpty()) {
showEditorStubPanel();
} else {
selectRecipe();
}
}
});
}
/** {@inheritDoc} */
@Override
public void onCloneButtonClicked() {
RecipeDescriptor selectedRecipeDescriptor = recipes.get(selectedRecipe);
String recipeName = NameGenerator.generateCopy(selectedRecipeDescriptor.getName(), recipes.keySet());
NewRecipe newRecipe = dtoFactory.createDto(NewRecipe.class)
.withType(selectedRecipeDescriptor.getType())
.withScript(selectedRecipeDescriptor.getScript())
.withName(recipeName)
.withTags(selectedRecipeDescriptor.getTags());
createRecipe(newRecipe);
}
/** {@inheritDoc} */
@Override
public void onNewButtonClicked() {
RecipeEditorPanel stubPanel = recipesContainerPresenter.getEditorStubPanel();
String recipeName = NameGenerator.generateCustomRecipeName(recipes.keySet());
List<String> tags = stubPanel.getTags();
if (recipes.isEmpty() && !stubPanel.getName().isEmpty()) {
recipeName = stubPanel.getName();
stubPanel.setTags(Collections.<String>emptyList());
}
NewRecipe newRecipe = dtoFactory.createDto(NewRecipe.class)
.withType(RECIPE_TYPE)
.withScript(resources.recipeTemplate().getText())
.withName(recipeName)
.withTags(tags);
createRecipe(newRecipe);
}
private void createRecipe(@NotNull NewRecipe newRecipe) {
Promise<RecipeDescriptor> createRecipe = service.createRecipe(newRecipe);
createRecipe.then(new Operation<RecipeDescriptor>() {
@Override
public void apply(RecipeDescriptor recipeDescriptor) throws OperationException {
RecipeWidget createdRecipeWidget = addRecipe(recipeDescriptor);
onRecipeClicked(createdRecipeWidget);
}
});
createRecipe.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
if (arg.getMessage() != null) {
notificationManager.notify(locale.failedToCreateRecipe(), arg.getMessage(), FAIL, true);
}
}
});
}
/** {@inheritDoc} */
@Override
public void onSaveButtonClicked() {
RecipeEditorPanel editorPanel = recipesContainerPresenter.getEditorPanel(selectedRecipe);
RecipeDescriptor recipeDescriptor = selectedRecipe.getDescriptor();
final RecipeUpdate recipeUpdate = dtoFactory.createDto(RecipeUpdate.class)
.withId(recipeDescriptor.getId())
.withType(recipeDescriptor.getType())
.withScript(editorPanel.getScript())
.withName(editorPanel.getName())
.withTags(editorPanel.getTags());
Promise<RecipeDescriptor> updateRecipe = service.updateRecipe(recipeUpdate);
updateRecipe.then(new Operation<RecipeDescriptor>() {
@Override
public void apply(RecipeDescriptor recipeDescriptor) throws OperationException {
RecipeDescriptor selectedRecipeDescriptor = recipes.get(selectedRecipe);
selectedRecipeDescriptor.setScript(recipeDescriptor.getScript());
selectedRecipeDescriptor.setTags(recipeDescriptor.getTags());
selectedRecipeDescriptor.setName(recipeDescriptor.getName());
selectedRecipe.setName(recipeDescriptor.getName());
notificationManager.notify("Recipe \"" + recipeDescriptor.getName() + "\" was saved.");
}
});
updateRecipe.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError arg) throws OperationException {
if (arg.getMessage() != null) {
notificationManager.notify(locale.failedToSaveRecipe(), arg.getMessage(), FAIL, true);
}
}
});
}
/** {@inheritDoc} */
@Override
public void selectRecipe() {
LinkedList<RecipeWidget> list = new LinkedList<>(recipes.keySet());
if (list.isEmpty() || (selectedRecipe != null && selectedRecipe.equals(list.getFirst()))) {
return;
}
for (RecipeWidget recipe : recipes.keySet()) {
recipe.unSelect();
}
selectedRecipe = list.getFirst();
selectedRecipe.select();
}
/** {@inheritDoc} */
@NotNull
@Override
public String getTitle() {
return locale.viewRecipePanelTitle();
}
/** {@inheritDoc} */
@Override
public void setVisible(boolean visible) {
view.setVisible(visible);
}
/** {@inheritDoc} */
@Override
public IsWidget getView() {
return view;
}
/** {@inheritDoc} */
@Nullable
@Override
public ImageResource getTitleImage() {
return null;
}
@Nullable
@Override
public SVGResource getTitleSVGImage() {
return resources.recipesPartIcon();
}
/** {@inheritDoc} */
@Nullable
@Override
public String getTitleToolTip() {
return locale.viewRecipePanelTooltip();
}
/** {@inheritDoc} */
@Override
public void go(AcceptsOneWidget container) {
container.setWidget(view);
showRecipes();
}
/** {@inheritDoc} */
@Override
public void onRecipeClicked(@NotNull RecipeWidget recipeWidget) {
if (selectedRecipe != null && selectedRecipe.equals(recipeWidget)) {
return;
}
for (RecipeWidget recipe : recipes.keySet()) {
recipe.unSelect();
}
selectedRecipe = recipeWidget;
selectedRecipe.select();
recipesContainerPresenter.showEditorPanel(selectedRecipe);
RecipeEditorPanel recipePanel = recipesContainerPresenter.getEditorPanel(selectedRecipe);
recipePanel.setDelegate(this);
}
private void showEditorStubPanel() {
RecipeEditorPanel editorStubPanel = recipesContainerPresenter.getEditorStubPanel();
RecipeEditorView stubPanelView = (RecipeEditorView)editorStubPanel.getView();
stubPanelView.setName("");
stubPanelView.setTags(Collections.<String>emptyList());
editorStubPanel.setVisibleSaveCancelCloneDeleteBtns(false);
editorStubPanel.setDelegate(this);
recipesContainerPresenter.showEditorStubPanel();
}
/** {@inheritDoc} */
@Override
public void onActivePartChanged(ActivePartChangedEvent event) {
if (!(event.getActivePart() instanceof RecipePartPresenter)) {
return;
}
recipesContainerPresenter.showEditorPanel(selectedRecipe);
RecipeEditorPanel recipePanel = recipesContainerPresenter.getEditorPanel(selectedRecipe);
recipePanel.setDelegate(this);
}
}