/*
* Copyright 2017 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.kie.workbench.common.stunner.standalone.client.screens;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import com.google.gwt.logging.client.LogConfiguration;
import com.google.gwt.user.client.ui.IsWidget;
import org.kie.workbench.common.stunner.client.widgets.menu.dev.MenuDevCommandsBuilder;
import org.kie.workbench.common.stunner.client.widgets.presenters.session.SessionPresenter;
import org.kie.workbench.common.stunner.client.widgets.presenters.session.SessionPresenterFactory;
import org.kie.workbench.common.stunner.client.widgets.toolbar.impl.EditorToolbar;
import org.kie.workbench.common.stunner.client.widgets.views.session.ScreenErrorView;
import org.kie.workbench.common.stunner.client.widgets.views.session.ScreenPanelView;
import org.kie.workbench.common.stunner.core.api.DefinitionManager;
import org.kie.workbench.common.stunner.core.client.api.SessionManager;
import org.kie.workbench.common.stunner.core.client.canvas.CanvasHandler;
import org.kie.workbench.common.stunner.core.client.service.ClientFactoryService;
import org.kie.workbench.common.stunner.core.client.service.ClientRuntimeError;
import org.kie.workbench.common.stunner.core.client.service.ServiceCallback;
import org.kie.workbench.common.stunner.core.client.session.ClientFullSession;
import org.kie.workbench.common.stunner.core.client.session.ClientSession;
import org.kie.workbench.common.stunner.core.client.session.command.ClientSessionCommand;
import org.kie.workbench.common.stunner.core.client.session.event.OnSessionErrorEvent;
import org.kie.workbench.common.stunner.core.client.session.impl.AbstractClientFullSession;
import org.kie.workbench.common.stunner.core.client.session.impl.AbstractClientReadOnlySession;
import org.kie.workbench.common.stunner.core.diagram.Diagram;
import org.kie.workbench.common.stunner.core.diagram.Metadata;
import org.kie.workbench.common.stunner.core.diagram.MetadataImpl;
import org.kie.workbench.common.stunner.core.graph.Graph;
import org.kie.workbench.common.stunner.core.rule.RuleViolation;
import org.kie.workbench.common.stunner.core.util.UUID;
import org.kie.workbench.common.stunner.core.validation.DiagramElementViolation;
import org.kie.workbench.common.stunner.core.validation.Violation;
import org.kie.workbench.common.stunner.core.validation.impl.ValidationUtils;
import org.uberfire.client.annotations.WorkbenchContextId;
import org.uberfire.client.annotations.WorkbenchMenu;
import org.uberfire.client.annotations.WorkbenchPartTitle;
import org.uberfire.client.annotations.WorkbenchPartView;
import org.uberfire.client.annotations.WorkbenchScreen;
import org.uberfire.client.mvp.PlaceManager;
import org.uberfire.client.workbench.events.ChangeTitleWidgetEvent;
import org.uberfire.ext.widgets.common.client.common.BusyPopup;
import org.uberfire.lifecycle.OnClose;
import org.uberfire.lifecycle.OnFocus;
import org.uberfire.lifecycle.OnLostFocus;
import org.uberfire.lifecycle.OnOpen;
import org.uberfire.lifecycle.OnStartup;
import org.uberfire.mvp.Command;
import org.uberfire.mvp.PlaceRequest;
import org.uberfire.workbench.model.menu.MenuFactory;
import org.uberfire.workbench.model.menu.Menus;
import static java.util.logging.Level.FINE;
// TODO: i18n.
@Dependent
@WorkbenchScreen(identifier = SessionDiagramEditorScreen.SCREEN_ID)
public class SessionDiagramEditorScreen {
private static Logger LOGGER = Logger.getLogger(SessionDiagramEditorScreen.class.getName());
public static final String SCREEN_ID = "SessionDiagramEditorScreen";
private final DefinitionManager definitionManager;
private final ClientFactoryService clientFactoryServices;
private final ShowcaseDiagramService diagramService;
private final SessionManager sessionManager;
private final SessionPresenterFactory<Diagram, AbstractClientReadOnlySession, AbstractClientFullSession> sessionPresenterFactory;
private final PlaceManager placeManager;
private final Event<ChangeTitleWidgetEvent> changeTitleNotificationEvent;
private final MenuDevCommandsBuilder menuDevCommandsBuilder;
private final ScreenPanelView screenPanelView;
private final ScreenErrorView screenErrorView;
private SessionPresenter<AbstractClientFullSession, ?, Diagram> presenter;
private PlaceRequest placeRequest;
private String title = "Authoring Screen";
private Menus menu = null;
@Inject
public SessionDiagramEditorScreen(final DefinitionManager definitionManager,
final ClientFactoryService clientFactoryServices,
final ShowcaseDiagramService diagramService,
final SessionManager sessionManager,
final SessionPresenterFactory<Diagram, AbstractClientReadOnlySession, AbstractClientFullSession> sessionPresenterFactory,
final PlaceManager placeManager,
final Event<ChangeTitleWidgetEvent> changeTitleNotificationEvent,
final MenuDevCommandsBuilder menuDevCommandsBuilder,
final ScreenPanelView screenPanelView,
final ScreenErrorView screenErrorView) {
this.definitionManager = definitionManager;
this.clientFactoryServices = clientFactoryServices;
this.diagramService = diagramService;
this.sessionManager = sessionManager;
this.sessionPresenterFactory = sessionPresenterFactory;
this.placeManager = placeManager;
this.changeTitleNotificationEvent = changeTitleNotificationEvent;
this.menuDevCommandsBuilder = menuDevCommandsBuilder;
this.screenPanelView = screenPanelView;
this.screenErrorView = screenErrorView;
}
@PostConstruct
@SuppressWarnings("unchecked")
public void init() {
}
@OnStartup
public void onStartup(final PlaceRequest placeRequest) {
this.placeRequest = placeRequest;
this.menu = makeMenuBar();
final String name = placeRequest.getParameter("name",
"");
final boolean isCreate = name == null || name.trim().length() == 0;
final Command callback = () -> {
final Diagram diagram = getDiagram();
if (null != diagram) {
// Update screen title.
updateTitle(diagram.getMetadata().getTitle());
}
};
if (isCreate) {
final String defSetId = placeRequest.getParameter("defSetId",
"");
final String shapeSetd = placeRequest.getParameter("shapeSetId",
"");
final String title = placeRequest.getParameter("title",
"");
// Create a new diagram.
newDiagram(UUID.uuid(),
title,
defSetId,
shapeSetd,
callback);
} else {
// Load an existing diagram.
load(name,
callback);
}
}
private Menus makeMenuBar() {
final MenuFactory.TopLevelMenusBuilder<MenuFactory.MenuBuilder> m =
MenuFactory
.newTopLevelMenu("Save")
.respondsWith(getSaveCommand())
.endMenu();
if (menuDevCommandsBuilder.isEnabled()) {
m.newTopLevelMenu(menuDevCommandsBuilder.build()).endMenu();
}
return m.build();
}
private Command getSaveCommand() {
return this::validateAndSave;
}
private void validateAndSave() {
final Command save = this::save;
final EditorToolbar toolbar = (EditorToolbar) presenter.getToolbar();
toolbar
.getValidateToolbarCommand()
.execute(new ClientSessionCommand.Callback<Collection<DiagramElementViolation<RuleViolation>>>() {
@Override
public void onSuccess() {
log(Level.INFO,
"Validation success.");
save.execute();
}
@Override
public void onError(final Collection<DiagramElementViolation<RuleViolation>> violations) {
log(Level.WARNING,
"Validation failed [violations=" + violations.toString() + "].");
// Allow saving when only warnings founds.
final Violation.Type maxSeverity = ValidationUtils.getMaxSeverity(violations);
if (!maxSeverity.equals(Violation.Type.ERROR)) {
save.execute();
}
}
});
}
private void save() {
diagramService.save(getSession(),
new ServiceCallback<Diagram<Graph, Metadata>>() {
@Override
public void onSuccess(Diagram<Graph, Metadata> item) {
log(Level.INFO,
"Save operation finished for diagram [" + item.getName() + "].");
}
@Override
public void onError(ClientRuntimeError error) {
showError(error);
}
});
}
private void newDiagram(final String uuid,
final String title,
final String definitionSetId,
final String shapeSetId,
final Command callback) {
BusyPopup.showMessage("Loading");
final Metadata metadata = buildMetadata(definitionSetId,
shapeSetId,
title);
clientFactoryServices.newDiagram(uuid,
definitionSetId,
metadata,
new ServiceCallback<Diagram>() {
@Override
public void onSuccess(final Diagram diagram) {
final Metadata metadata = diagram.getMetadata();
metadata.setShapeSetId(shapeSetId);
metadata.setTitle(title);
final AbstractClientFullSession session = newSession(diagram);
presenter = sessionPresenterFactory.newPresenterEditor();
screenPanelView.setWidget(presenter.getView());
presenter
.withToolbar(true)
.withPalette(true)
.displayNotifications(type -> true)
.open(diagram,
session,
new ScreenPresenterCallback(callback));
}
@Override
public void onError(final ClientRuntimeError error) {
showError(error);
callback.execute();
}
});
}
private Metadata buildMetadata(final String defSetId,
final String shapeSetId,
final String title) {
return new MetadataImpl.MetadataImplBuilder(defSetId,
definitionManager)
.setTitle(title)
.setShapeSetId(shapeSetId)
.build();
}
private void load(final String name,
final Command callback) {
BusyPopup.showMessage("Loading");
diagramService.loadByName(name,
new ServiceCallback<Diagram>() {
@Override
public void onSuccess(final Diagram diagram) {
final AbstractClientFullSession session = newSession(diagram);
presenter = sessionPresenterFactory.newPresenterEditor();
screenPanelView.setWidget(presenter.getView());
presenter
.withToolbar(true)
.withPalette(true)
.displayNotifications(type -> true)
.open(diagram,
session,
new ScreenPresenterCallback(callback));
}
@Override
public void onError(final ClientRuntimeError error) {
showError(error);
callback.execute();
}
});
}
@OnOpen
public void onOpen() {
resume();
}
@OnFocus
public void onFocus() {
if (null != getSession() && !isSameSession(sessionManager.getCurrentSession())) {
sessionManager.open(getSession());
} else if (null != getSession()) {
log(FINE,
"Session already active, no action.");
}
}
private boolean isSameSession(final ClientSession other) {
return null != other && null != getSession() && other.equals(getSession());
}
@OnLostFocus
public void OnLostFocus() {
}
@OnClose
public void onClose() {
destroySession();
}
@WorkbenchMenu
public Menus getMenu() {
return menu;
}
private void resume() {
if (null != getSession()) {
sessionManager.resume(getSession());
}
}
private void destroySession() {
presenter.destroy();
}
@WorkbenchPartTitle
public String getTitle() {
return title;
}
@WorkbenchPartView
public IsWidget getWidget() {
return screenPanelView.asWidget();
}
@WorkbenchContextId
public String getMyContextRef() {
return "sessionDiagramEditorScreenContext";
}
private final class ScreenPresenterCallback implements SessionPresenter.SessionPresenterCallback<AbstractClientFullSession, Diagram> {
private final Command callback;
private ScreenPresenterCallback(final Command callback) {
this.callback = callback;
}
@Override
public void afterSessionOpened() {
}
@Override
public void afterCanvasInitialized() {
}
@Override
public void onSuccess() {
BusyPopup.close();
callback.execute();
}
@Override
public void onError(final ClientRuntimeError error) {
showError(error);
callback.execute();
}
}
private void updateTitle(final String title) {
// Change screen title.
SessionDiagramEditorScreen.this.title = title;
changeTitleNotificationEvent.fire(new ChangeTitleWidgetEvent(placeRequest,
this.title));
}
private AbstractClientFullSession newSession(final Diagram diagram) {
return (AbstractClientFullSession) sessionManager.getSessionFactory(diagram,
ClientFullSession.class).newSession();
}
private AbstractClientFullSession getSession() {
return null != presenter ? presenter.getInstance() : null;
}
private CanvasHandler getCanvasHandler() {
return null != sessionManager.getCurrentSession() ? sessionManager.getCurrentSession().getCanvasHandler() : null;
}
private Diagram getDiagram() {
return null != getCanvasHandler() ? getCanvasHandler().getDiagram() : null;
}
private void showError(final ClientRuntimeError error) {
screenErrorView.showError(error);
screenPanelView.setWidget(screenErrorView.asWidget());
log(Level.SEVERE,
error.toString());
BusyPopup.close();
}
void onSessionErrorEvent(@Observes OnSessionErrorEvent errorEvent) {
if (isSameSession(errorEvent.getSession())) {
showError(errorEvent.getError());
// TODO executeWithConfirm( "An error happened [" + errorEvent.getError() + "]. Do you want" +
// "to refresh the diagram (Last changes can be lost)? ", this::menu_refresh );
}
}
private void log(final Level level,
final String message) {
if (LogConfiguration.loggingIsEnabled()) {
LOGGER.log(level,
message);
}
}
}