/******************************************************************************* * Copyright (c) 2012-2015 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.actions; import org.eclipse.che.api.analytics.client.logger.AnalyticsEventLogger; import org.eclipse.che.api.project.gwt.client.ProjectServiceClient; import org.eclipse.che.api.project.gwt.client.QueryExpression; import org.eclipse.che.api.project.shared.dto.ItemReference; import org.eclipse.che.api.runner.dto.ApplicationProcessDescriptor; import org.eclipse.che.api.runner.gwt.client.RunnerServiceClient; import org.eclipse.che.ide.CoreLocalizationConstant; import org.eclipse.che.ide.part.projectexplorer.ProjectListStructure; import org.eclipse.che.ide.Resources; import org.eclipse.che.ide.CoreLocalizationConstant; import org.eclipse.che.ide.Resources; import org.eclipse.che.ide.api.action.Action; import org.eclipse.che.ide.api.action.ActionEvent; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.editor.EditorAgent; import org.eclipse.che.ide.api.editor.EditorPartPresenter; import org.eclipse.che.ide.api.notification.Notification; import org.eclipse.che.ide.api.notification.NotificationManager; import org.eclipse.che.ide.api.project.tree.AbstractTreeNode; import org.eclipse.che.ide.api.project.tree.TreeStructure; import org.eclipse.che.ide.api.project.tree.TreeNode; import org.eclipse.che.ide.api.project.tree.VirtualFile; import org.eclipse.che.ide.api.project.tree.generic.FileNode; import org.eclipse.che.ide.api.project.tree.generic.FolderNode; import org.eclipse.che.ide.api.project.tree.generic.ItemNode; import org.eclipse.che.ide.api.project.tree.generic.ProjectNode; import org.eclipse.che.ide.api.project.tree.generic.StorableNode; import org.eclipse.che.ide.api.selection.Selection; import org.eclipse.che.ide.api.selection.SelectionAgent; import org.eclipse.che.ide.collections.Array; import org.eclipse.che.ide.collections.Collections; import org.eclipse.che.ide.collections.StringMap; import org.eclipse.che.ide.part.projectexplorer.ProjectListStructure; import org.eclipse.che.ide.rest.AsyncRequestCallback; import org.eclipse.che.ide.rest.DtoUnmarshallerFactory; import org.eclipse.che.ide.rest.Unmarshallable; import org.eclipse.che.ide.ui.dialogs.DialogFactory; import org.eclipse.che.ide.ui.dialogs.InputCallback; import org.eclipse.che.ide.ui.dialogs.input.InputDialog; import org.eclipse.che.ide.ui.dialogs.input.InputValidator; import org.eclipse.che.ide.util.NameUtils; import org.eclipse.che.ide.util.loging.Log; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; import com.google.inject.Singleton; import javax.annotation.Nullable; import static org.eclipse.che.api.runner.ApplicationStatus.NEW; import static org.eclipse.che.api.runner.ApplicationStatus.RUNNING; import static org.eclipse.che.ide.api.notification.Notification.Type.ERROR; import static org.eclipse.che.ide.api.project.tree.TreeNode.RenameCallback; /** * Action for renaming an item which is selected in 'Project Explorer'. * * @author Artem Zatsarynnyy */ @Singleton public class RenameItemAction extends Action { private final AnalyticsEventLogger eventLogger; private final NotificationManager notificationManager; private final EditorAgent editorAgent; private final CoreLocalizationConstant localization; private final ProjectServiceClient projectServiceClient; private final RunnerServiceClient runnerServiceClient; private final DtoUnmarshallerFactory dtoUnmarshallerFactory; private final DialogFactory dialogFactory; private final AppContext appContext; private final SelectionAgent selectionAgent; private final InputValidator fileNameValidator; private final InputValidator folderNameValidator; private final InputValidator projectNameValidator; @Inject public RenameItemAction(Resources resources, AnalyticsEventLogger eventLogger, SelectionAgent selectionAgent, NotificationManager notificationManager, EditorAgent editorAgent, CoreLocalizationConstant localization, ProjectServiceClient projectServiceClient, RunnerServiceClient runnerServiceClient, DtoUnmarshallerFactory dtoUnmarshallerFactory, DialogFactory dialogFactory, AppContext appContext) { super(localization.renameItemActionText(), localization.renameItemActionDescription(), null, resources.rename()); this.selectionAgent = selectionAgent; this.eventLogger = eventLogger; this.notificationManager = notificationManager; this.editorAgent = editorAgent; this.localization = localization; this.projectServiceClient = projectServiceClient; this.runnerServiceClient = runnerServiceClient; this.dtoUnmarshallerFactory = dtoUnmarshallerFactory; this.dialogFactory = dialogFactory; this.appContext = appContext; this.fileNameValidator = new FileNameValidator(); this.folderNameValidator = new FolderNameValidator(); this.projectNameValidator = new ProjectNameValidator(); } /** {@inheritDoc} */ @Override public void actionPerformed(ActionEvent e) { eventLogger.log(this); Selection<?> selection = selectionAgent.getSelection(); if (selection != null && selection.getFirstElement() != null && selection.getFirstElement() instanceof StorableNode) { final StorableNode selectedNode = (StorableNode)selection.getFirstElement(); if (selectedNode instanceof ProjectNode) { dialogFactory.createMessageDialog("", localization.closeProjectBeforeRenaming(), null).show(); } else if (selectedNode instanceof ProjectListStructure.ProjectNode) { checkRunningProcessesForProject(selectedNode, new AsyncCallback<Boolean>() { @Override public void onSuccess(Boolean hasRunningProcesses) { if (hasRunningProcesses) { dialogFactory.createMessageDialog("", localization.stopProcessesBeforeRenamingProject(), null).show(); } else { askForRenamingNode(selectedNode); } } @Override public void onFailure(Throwable caught) { askForRenamingNode(selectedNode); } }); } else { askForRenamingNode(selectedNode); } } } /** {@inheritDoc} */ @Override public void update(ActionEvent e) { if ((appContext.getCurrentProject() == null && !appContext.getCurrentUser().isUserPermanent()) || (appContext.getCurrentProject() != null && appContext.getCurrentProject().isReadOnly())) { e.getPresentation().setVisible(true); e.getPresentation().setEnabled(false); return; } boolean enabled = false; Selection<?> selection = selectionAgent.getSelection(); if (selection != null && selection.getFirstElement() instanceof AbstractTreeNode) { enabled = selection.getFirstElement() instanceof StorableNode && ((AbstractTreeNode)selection.getFirstElement()).isRenamable(); } e.getPresentation().setEnabled(enabled); } private void askForRenamingNode(final StorableNode nodeToRename) { final InputCallback inputCallback = new InputCallback() { @Override public void accepted(final String value) { ItemReference itemReferenceBeforeRenaming = null; if (nodeToRename instanceof ItemNode) { itemReferenceBeforeRenaming = ((ItemNode)nodeToRename).getData(); } final ItemReference finalItemReferenceBeforeRenaming = itemReferenceBeforeRenaming; nodeToRename.rename(value, new RenameCallback() { @Override public void onRenamed() { if (finalItemReferenceBeforeRenaming != null) { checkOpenedFiles(finalItemReferenceBeforeRenaming, value); } } @Override public void onFailure(Throwable caught) { notificationManager.showNotification(new Notification(caught.getMessage(), ERROR)); } }); } }; final int selectionLength = nodeToRename.getName().indexOf('.') >= 0 ? nodeToRename.getName().lastIndexOf('.') : nodeToRename.getName().length(); InputDialog inputDialog = dialogFactory.createInputDialog(getDialogTitle(nodeToRename), localization.renameDialogNewNameLabel(), nodeToRename.getName(), 0, selectionLength, inputCallback, null); if (nodeToRename instanceof FileNode) { inputDialog.withValidator(fileNameValidator); } else if (nodeToRename instanceof FolderNode) { inputDialog.withValidator(folderNameValidator); } else if (nodeToRename instanceof ProjectNode || nodeToRename instanceof ProjectListStructure.ProjectNode) { inputDialog.withValidator(projectNameValidator); } inputDialog.show(); } /** * Check whether project has any running processes. * * @param projectNode * project to check * @param callback * callback returns true if project has any running processes and false - otherwise */ private void checkRunningProcessesForProject(StorableNode projectNode, final AsyncCallback<Boolean> callback) { Unmarshallable<Array<ApplicationProcessDescriptor>> unmarshaller = dtoUnmarshallerFactory.newArrayUnmarshaller(ApplicationProcessDescriptor.class); runnerServiceClient.getRunningProcesses(projectNode.getPath(), new AsyncRequestCallback<Array<ApplicationProcessDescriptor>>(unmarshaller) { @Override protected void onSuccess(Array<ApplicationProcessDescriptor> result) { boolean hasRunningProcesses = false; for (ApplicationProcessDescriptor descriptor : result.asIterable()) { if (descriptor.getStatus() == NEW || descriptor.getStatus() == RUNNING) { hasRunningProcesses = true; break; } } callback.onSuccess(hasRunningProcesses); } @Override protected void onFailure(Throwable exception) { callback.onFailure(exception); } }); } private void checkOpenedFiles(ItemReference itemBeforeRenaming, String newName) { final String itemPathBeforeRenaming = itemBeforeRenaming.getPath(); final String parentPathBeforeRenaming = itemPathBeforeRenaming.substring(0, itemPathBeforeRenaming.length() - itemBeforeRenaming.getName().length()); final String itemPathAfterRenaming = parentPathBeforeRenaming + newName; if ("file".equals(itemBeforeRenaming.getType())) { checkEditor(itemPathBeforeRenaming, itemPathAfterRenaming); } else if ("folder".equals(itemBeforeRenaming.getType())) { QueryExpression query = new QueryExpression().setPath(itemPathAfterRenaming); Unmarshallable<Array<ItemReference>> unmarshaller = dtoUnmarshallerFactory.newArrayUnmarshaller(ItemReference.class); projectServiceClient.search(query, new AsyncRequestCallback<Array<ItemReference>>(unmarshaller) { @Override protected void onSuccess(Array<ItemReference> result) { StringMap<ItemReference> children = Collections.createStringMap(); for (ItemReference itemReference : result.asIterable()) { children.put(itemReference.getPath(), itemReference); } for (EditorPartPresenter editor : editorAgent.getOpenedEditors().getValues().asIterable()) { VirtualFile openedFile = editor.getEditorInput().getFile(); if (children.get(openedFile.getPath()) != null) { String pathBeforeRenaming = openedFile.getPath().replaceFirst(itemPathAfterRenaming, itemPathBeforeRenaming); checkEditor(pathBeforeRenaming, openedFile.getPath()); } } } @Override protected void onFailure(Throwable exception) { Log.error(RenameItemAction.class, exception); } }); } } private void checkEditor(String filePathBeforeRename, String filePathAfterRename) { final EditorPartPresenter editor = editorAgent.getOpenedEditors().remove(filePathBeforeRename); if (editor != null) { TreeStructure currentTreeStructure = appContext.getCurrentProject().getCurrentTree(); currentTreeStructure.getNodeByPath(filePathAfterRename, new AsyncCallback<TreeNode<?>>() { @Override public void onFailure(Throwable caught) { } @Override public void onSuccess(TreeNode<?> result) { FileNode fileNode = (FileNode)result; editor.getEditorInput().setFile(fileNode); editorAgent.getOpenedEditors().put(fileNode.getPath(), editor); editor.onFileChanged(); } }); } } private String getDialogTitle(StorableNode node) { if (node instanceof FileNode) { return localization.renameFileDialogTitle(); } else if (node instanceof FolderNode) { return localization.renameFolderDialogTitle(); } else if (node instanceof ProjectNode || node instanceof ProjectListStructure.ProjectNode) { return localization.renameProjectDialogTitle(); } return localization.renameNodeDialogTitle(); } private class FileNameValidator implements InputValidator { @Nullable @Override public Violation validate(String value) { if (!NameUtils.checkFileName(value)) { return new Violation() { @Override public String getMessage() { return localization.invalidName(); } }; } return null; } } private class FolderNameValidator implements InputValidator { @Nullable @Override public Violation validate(String value) { if (!NameUtils.checkFolderName(value)) { return new Violation() { @Override public String getMessage() { return localization.invalidName(); } }; } return null; } } private class ProjectNameValidator implements InputValidator { @Nullable @Override public Violation validate(String value) { if (!NameUtils.checkProjectName(value)) { return new Violation() { @Override public String getMessage() { return localization.invalidName(); } }; } return null; } } }