/*******************************************************************************
* 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.newresource;
import org.eclipse.che.api.analytics.client.logger.AnalyticsEventLogger;
import org.eclipse.che.api.project.gwt.client.ProjectServiceClient;
import org.eclipse.che.api.project.shared.dto.ItemReference;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.ProjectAction;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.app.CurrentProject;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.event.ItemEvent;
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.ItemNode;
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.json.JsonHelper;
import org.eclipse.che.ide.rest.AsyncRequestCallback;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
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.web.bindery.event.shared.EventBus;
import org.vectomatic.dom.svg.ui.SVGResource;
import javax.annotation.Nullable;
import static org.eclipse.che.ide.api.event.ItemEvent.ItemOperation.CREATED;
/**
* Implementation of an {@link Action} that provides an ability to create new resource (e.g. file, folder).
* After performing this action, it asks user for the resource's name
* and then creates resource in the selected folder.
*
* @author Artem Zatsarynnyy
*/
public abstract class AbstractNewResourceAction extends ProjectAction {
protected final InputValidator fileNameValidator;
protected final InputValidator folderNameValidator;
protected final String title;
protected SelectionAgent selectionAgent;
protected EditorAgent editorAgent;
protected ProjectServiceClient projectServiceClient;
protected EventBus eventBus;
protected AppContext appContext;
protected AnalyticsEventLogger eventLogger;
protected DtoUnmarshallerFactory dtoUnmarshallerFactory;
protected DialogFactory dialogFactory;
protected CoreLocalizationConstant coreLocalizationConstant;
/**
* Creates new action.
*
* @param title
* action's title
* @param description
* action's description
* @param svgIcon
* action's SVG icon
*/
public AbstractNewResourceAction(String title, String description, @Nullable SVGResource svgIcon) {
super(title, description, svgIcon);
fileNameValidator = new FileNameValidator();
folderNameValidator = new FolderNameValidator();
this.title = title;
}
@Override
public void actionPerformed(ActionEvent e) {
if (eventLogger != null) {
eventLogger.log(this);
}
InputDialog inputDialog = dialogFactory.createInputDialog(
coreLocalizationConstant.newResourceTitle(title),
coreLocalizationConstant.newResourceLabel(title.toLowerCase()),
new InputCallback() {
@Override
public void accepted(String value) {
onAccepted(value);
}
}, null).withValidator(fileNameValidator);
inputDialog.show();
}
private void onAccepted(String value) {
final String name = getExtension().isEmpty() ? value : value + '.' + getExtension();
final StorableNode parent = getNewResourceParent();
if (parent == null) {
throw new IllegalStateException("No selected parent.");
}
projectServiceClient.createFile(
parent.getPath(), name, getDefaultContent(), getMimeType(),
new AsyncRequestCallback<ItemReference>(dtoUnmarshallerFactory.newUnmarshaller(ItemReference.class)) {
@Override
protected void onSuccess(final ItemReference result) {
onFileCreated(result);
}
@Override
protected void onFailure(Throwable exception) {
dialogFactory.createMessageDialog("", JsonHelper.parseJsonMessage(exception.getMessage()), null).show();
}
});
}
private void onFileCreated(final ItemReference result) {
final CurrentProject currentProject = appContext.getCurrentProject();
if (currentProject == null) {
throw new IllegalStateException("No opened project.");
}
currentProject.getCurrentTree().getNodeByPath(result.getPath(), new AsyncCallback<TreeNode<?>>() {
@Override
public void onSuccess(TreeNode<?> treeNode) {
eventBus.fireEvent(new ItemEvent((ItemNode)treeNode, CREATED));
if ("file".equals(result.getType())) {
editorAgent.openEditor((VirtualFile)treeNode);
}
}
@Override
public void onFailure(Throwable throwable) {
Log.error(AbstractNewResourceAction.class, throwable);
}
});
}
@Override
public void updateProjectAction(ActionEvent e) {
e.getPresentation().setEnabled(getNewResourceParent() != null);
}
/**
* Returns extension (without dot) for a new resource.
* By default, returns an empty string.
*/
protected String getExtension() {
return "";
}
/**
* Returns default content for a new resource.
* By default, returns an empty string.
*/
protected String getDefaultContent() {
return "";
}
/**
* Returns MIME-type for a new resource.
* By default, returns {@code null}.
*/
protected String getMimeType() {
return null;
}
/** Returns parent for creating new item or {@code null} if resource can not be created. */
@Nullable
protected StorableNode getNewResourceParent() {
Selection<?> selection = selectionAgent.getSelection();
if (selection != null && selection.getFirstElement() != null) {
if (selection.getFirstElement() instanceof StorableNode) {
final StorableNode selectedNode = (StorableNode)selection.getFirstElement();
if (selectedNode instanceof FileNode) {
return (StorableNode)selectedNode.getParent();
}
return selectedNode;
}
}
return null;
}
@Inject
private void init(SelectionAgent selectionAgent,
EditorAgent editorAgent,
ProjectServiceClient projectServiceClient,
EventBus eventBus,
AppContext appContext,
AnalyticsEventLogger eventLogger,
DtoUnmarshallerFactory dtoUnmarshallerFactory,
DialogFactory dialogFactory,
CoreLocalizationConstant coreLocalizationConstant) {
this.selectionAgent = selectionAgent;
this.editorAgent = editorAgent;
this.projectServiceClient = projectServiceClient;
this.eventBus = eventBus;
this.appContext = appContext;
this.eventLogger = eventLogger;
this.dtoUnmarshallerFactory = dtoUnmarshallerFactory;
this.dialogFactory = dialogFactory;
this.coreLocalizationConstant = coreLocalizationConstant;
}
private class FileNameValidator implements InputValidator {
@Nullable
@Override
public Violation validate(String value) {
if (!NameUtils.checkFileName(value)) {
return new Violation() {
@Override
public String getMessage() {
return coreLocalizationConstant.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 coreLocalizationConstant.invalidName();
}
};
}
return null;
}
}
}