/*******************************************************************************
* 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.ide.command.manager;
import com.google.gwt.json.client.JSONException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.machine.shared.dto.CommandDto;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseProvider;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandType;
import org.eclipse.che.ide.api.project.MutableProjectConfig;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.util.loging.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.eclipse.che.api.project.shared.Constants.COMMANDS_ATTRIBUTE_NAME;
/** Responsible for managing the commands which are stored with projects. */
@Singleton
class ProjectCommandManagerDelegate {
private final DtoFactory dtoFactory;
private final PromiseProvider promiseProvider;
@Inject
ProjectCommandManagerDelegate(DtoFactory dtoFactory, PromiseProvider promiseProvider) {
this.dtoFactory = dtoFactory;
this.promiseProvider = promiseProvider;
}
/** Returns commands for the specified {@code project}. */
List<CommandImpl> getCommands(Project project) {
List<String> attrValues = project.getAttributes(COMMANDS_ATTRIBUTE_NAME);
if (attrValues == null) {
return new ArrayList<>();
}
Map<String, CommandImpl> commands = new HashMap<>(attrValues.size());
for (String commandJson : attrValues) {
try {
CommandDto commandDto = dtoFactory.createDtoFromJson(commandJson, CommandDto.class);
commands.put(commandDto.getName(), new CommandImpl(commandDto));
} catch (JSONException e) {
Log.error(ProjectCommandManagerDelegate.class, "Unable to parse command of project '" +
project.getPath() + "': " + commandJson + ". " + e.getMessage());
}
}
return new ArrayList<>(commands.values());
}
/**
* Creates new command of the specified type.
* <p><b>Note</b> that command's name will be generated by {@link CommandManager}
* and command line will be provided by an appropriate {@link CommandType}.
*/
Promise<CommandImpl> createCommand(Project project, final CommandImpl newCommand) {
final List<CommandImpl> commands = getCommands(project);
for (CommandImpl projectCommand : commands) {
if (projectCommand.getName().equals(newCommand.getName())) {
return promiseProvider.reject(new Exception("Command '" + newCommand.getName() +
"' is already associated to the project '" + project.getName() + "'"));
}
}
commands.add(newCommand);
return updateProject(project, commands).then((Function<Void, CommandImpl>)arg -> newCommand);
}
/** Updates the specified {@code project} with the given {@code commands}. */
private Promise<Void> updateProject(Project project, List<CommandImpl> commands) {
MutableProjectConfig config = new MutableProjectConfig(project);
Map<String, List<String>> attributes = config.getAttributes();
List<String> attrValue = new ArrayList<>(attributes.size());
for (CommandImpl command : commands) {
CommandDto commandDto = dtoFactory.createDto(CommandDto.class)
.withName(command.getName())
.withType(command.getType())
.withCommandLine(command.getCommandLine())
.withAttributes(command.getAttributes());
attrValue.add(dtoFactory.toJson(commandDto));
}
attributes.put(COMMANDS_ATTRIBUTE_NAME, attrValue);
return project.update()
.withBody(config)
.send()
.then((Function<Project, Void>)arg -> null);
}
/**
* Updates the command with the specified {@code name} by replacing it with the given {@code command}.
* <p><b>Note</b> that name of the updated command may differ from the name provided by the given {@code command}
* in order to prevent name duplication.
*/
Promise<CommandImpl> updateCommand(Project project, final CommandImpl command) {
final List<CommandImpl> projectCommands = getCommands(project);
if (projectCommands.isEmpty()) {
return promiseProvider.reject(new Exception("Command '" + command.getName() + "' is not associated with the project '" +
project.getName() + "'"));
}
final List<CommandImpl> commandsToUpdate = new ArrayList<>();
for (CommandImpl projectCommand : projectCommands) {
// skip existed command with the same name
if (!command.getName().equals(projectCommand.getName())) {
commandsToUpdate.add(projectCommand);
}
}
commandsToUpdate.add(command);
return updateProject(project, new ArrayList<>(commandsToUpdate))
.then((Function<Void, CommandImpl>)arg -> command);
}
/** Removes the command with the specified {@code commandName}. */
Promise<Void> removeCommand(Project project, String commandName) {
final List<CommandImpl> commands = getCommands(project);
if (commands.isEmpty()) {
return promiseProvider.reject(new Exception("Command '" + commandName + "' is not associated with the project '" +
project.getName() + "'"));
}
final List<CommandImpl> commandsToUpdate = new ArrayList<>();
for (CommandImpl projectCommand : commands) {
if (!commandName.equals(projectCommand.getName())) {
commandsToUpdate.add(projectCommand);
}
}
return updateProject(project, new ArrayList<>(commandsToUpdate));
}
}