/******************************************************************************* * 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)); } }