blob: ef5eba07d999e9ee971f34b06838fc442884ebc2 [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*/
package org.eclipse.osbp.xtext.dialogdsl.common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.osbp.core.api.persistence.IPersistenceService;
import org.eclipse.osbp.dsl.common.datatypes.IDto;
import org.eclipse.osbp.ecview.core.common.context.IViewContext;
import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddable;
import org.eclipse.osbp.ecview.core.common.model.core.YField;
import org.eclipse.osbp.ecview.core.common.model.core.YFocusable;
import org.eclipse.osbp.ecview.core.common.model.core.YView;
import org.eclipse.osbp.ecview.core.common.notification.AbstractReloadRequestService;
import org.eclipse.osbp.ecview.core.common.notification.IReloadRequestService;
import org.eclipse.osbp.ecview.core.common.store.IViewStore;
import org.eclipse.osbp.ecview.core.common.validation.IFieldValidationManager;
import org.eclipse.osbp.ecview.extension.model.YDelegatingLayoutingStrategy;
import org.eclipse.osbp.ecview.extension.model.YECviewFactory;
import org.eclipse.osbp.ecview.extension.model.YStrategyLayout;
import org.eclipse.osbp.ecview.extension.presentation.vaadin.components.common.ECViewComponent;
import org.eclipse.osbp.eventbroker.EventBrokerMsg;
import org.eclipse.osbp.runtime.common.annotations.DirtyStateAdapter;
import org.eclipse.osbp.runtime.common.annotations.DtoUtils;
import org.eclipse.osbp.runtime.common.annotations.IsValid;
import org.eclipse.osbp.runtime.common.event.EventDispatcherEvent;
import org.eclipse.osbp.runtime.common.event.EventDispatcherEvent.EventDispatcherDataTag;
import org.eclipse.osbp.runtime.common.event.IEventDispatcher;
import org.eclipse.osbp.runtime.common.filter.IDTOServiceWithMutablePersistence;
import org.eclipse.osbp.runtime.common.i18n.II18nService;
import org.eclipse.osbp.runtime.common.validation.IStatus;
import org.eclipse.osbp.runtime.web.vaadin.components.dialogs.AcceptDeleteDialog;
import org.eclipse.osbp.runtime.web.vaadin.components.dialogs.AcceptLoosingDataDialog;
import org.eclipse.osbp.runtime.web.vaadin.components.dialogs.AcceptSaveDialog;
import org.eclipse.osbp.ui.api.e4.IE4Dialog;
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService;
import org.eclipse.osbp.ui.api.perspective.IPerspectiveProvider;
import org.eclipse.osbp.ui.api.report.IReportProvider;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService.ThemeResourceType;
import org.eclipse.osbp.ui.api.user.IUser;
import org.eclipse.osbp.ui.common.helper.ICommonUIHelper;
import org.eclipse.osbp.utils.vaadin.ViewLayoutManager;
import org.eclipse.osbp.vaaclipse.addons.common.api.status.IStatusManager;
import org.eclipse.osbp.vaaclipse.addons.common.api.status.IStatusScope;
import org.eclipse.osbp.vaaclipse.common.ecview.E4EventBrokerAdapter;
import org.eclipse.osbp.vaaclipse.common.ecview.ECViewToE4ServiceBridge;
import org.eclipse.osbp.xtext.action.common.IToolbarAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
/**
* The Class AbstractEmbeddedDialog.
*
* @param <DTO>
* the generic type
*/
@SuppressWarnings("all")
public abstract class AbstractEmbeddedDialog<DTO>
implements IUser.UserLocaleListener, IEventDispatcher.Receiver, IFieldValidationManager.Listener, IE4Dialog {
/** The Constant log. */
private final static Logger log = LoggerFactory.getLogger("vaaclipseDialog");
private static final String BEAN_SLOT__VALIDATION_ERROR = "validationError";
/** The user id. */
@Inject
@Named("userId")
@Optional
String userId;
/** The event broker. */
@Inject
private IEventBroker eventBroker;
/** The rendering engine. */
@Inject
private IPresentationEngine renderingEngine;
/** The persistence service. */
@Inject
private IPersistenceService persistenceService;
/** The theme resource service. */
@Inject
private IThemeResourceService themeResourceService;
@Inject
private IEventDispatcher eventDispatcher;
/** The common ui helper. */
@Inject
private ICommonUIHelper commonUIHelper;
/** The translator class service. */
@Inject
private IDSLMetadataService dslMetadataService;
/** The report provider. */
@Inject
IReportProvider reportProvider;
/** The i18n service. */
@Inject
private II18nService i18nService;
/** The view store. */
@Inject
private IViewStore viewStore;
/** The eclipse context. */
@Inject
private IEclipseContext eclipseContext;
@Inject
private IUser user;
@Inject
private IStatusManager statusManager;
/** The strategy event handler. */
private StrategyEventHandler strategyEventHandler;
/** The view context. */
// ecview stuff
private IViewContext viewContext;
/** The ecview component. */
private ECViewComponent ecviewComponent;
/** The rendering params. */
private Map<String, Object> renderingParams;
/** The dto service. */
private IDTOServiceWithMutablePersistence<DTO> dtoService;
/** The layout manager. */
private ViewLayoutManager layoutManager;
/** The ecview field validation manager. */
private IFieldValidationManager ecviewFieldValidationManager;
/** The validation result. */
private Set<IStatus> validationResult;
/** The uuid. */
protected String uuid = UUID.randomUUID().toString();
private Label dialogLabel;
private Window selectWindow;
private ArrayList<Button> taskButtons;
private boolean newCreatedDto = false;
private boolean manualInput = false;
private List<Consumer<DTO>> saveListeners;
private IReloadRequestService reloadRequestService = new AbstractReloadRequestService() {
@Override
public void requestReload(YEmbeddable component, Object newDto) {
updateData(newDto);
}
};
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.osgi.hybrid.api.AbstractHybridVaaclipseView#createView(
* com.vaadin.ui.VerticalLayout)
*/
public void createView(final VerticalLayout parent) {
eclipseContext.set(IE4Dialog.class, this);
layoutManager = new ViewLayoutManager();
layoutManager.init(parent);
if (viewStore != null) {
ecviewComponent = new ECViewComponent(new ECViewComponent.ViewProviderCallback() {
@Override
public YView getView(String viewId, Map<String, Object> properties) {
// use the view store to find a user saved view
return viewStore.getMostRecentView(userId, viewId);
}
});
} else {
ecviewComponent = new ECViewComponent();
}
ecviewComponent.setSizeFull();
layoutManager.getDataArea().addComponent(ecviewComponent);
taskButtons = new ArrayList<Button>();
HorizontalLayout tools = new HorizontalLayout();
tools.addStyleName("os-embedded-toolbar");
tools.setSpacing(false);
Button b = new Button(null, new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
DTO savedDto = (DTO) viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
dtoService.update(savedDto);
DTO newInstance = dtoService.get(getIdForDto(savedDto));
ecviewComponent.setValue(getViewId(), newInstance, renderingParams);
notifySaveListeners(newInstance);
}
});
b.setIcon(themeResourceService.getThemeResource("dssave", ThemeResourceType.ICON));
b.setDescription(null);
b.setEnabled(true);
b.addStyleName("v-button-small");
tools.addComponent(b);
taskButtons.add(b);
layoutManager.getDataArea().addComponentAsFirst(tools);
// create the rendering params used for ECView
renderingParams = createRenderingParams();
// create the event handlers
strategyEventHandler = new StrategyEventHandler();
}
@Override
public void validationChanged(IFieldValidationManager.Event event) {
// add them to the Vaaclipse status manager
IStatusScope scope = statusManager.getActiveScope();
scope.modifyStatus(event.getOldStatus(), event.getNewStatus());
applyValidationResult();
}
/**
* Adds a new save listener.
*
* @param listener
*/
public void addSaveListener(Consumer<DTO> listener) {
if (saveListeners == null) {
saveListeners = new ArrayList<>();
}
saveListeners.add(listener);
}
/**
* Removes the given save listener.
*
* @param listener
*/
public void removeSaveListener(Consumer<DTO> listener) {
if (saveListeners == null) {
return;
}
saveListeners.remove(listener);
}
protected void notifySaveListeners(DTO dto) {
if (saveListeners == null) {
return;
}
for (Consumer<DTO> listener : saveListeners.toArray(new Consumer[saveListeners.size()])) {
listener.accept(dto);
}
}
/**
* Apply the validation result to the view.
*/
protected void applyValidationResult() {
// set the validation result
viewContext.setBean(BEAN_SLOT__VALIDATION_ERROR, Boolean.FALSE);
for (IStatus status : statusManager.getActiveScope().getAllStatus()) {
if (status.isError()) {
viewContext.setBean(BEAN_SLOT__VALIDATION_ERROR, Boolean.TRUE);
break;
}
}
}
@IsValid
public boolean isDataValid() {
if (viewContext == null) {
return true;
}
Boolean result = (Boolean) viewContext.getBean(BEAN_SLOT__VALIDATION_ERROR);
if (result == null) {
return true;
}
return (boolean) !result;
}
@Focus
public void setFocus() {
if (viewContext != null) {
YView yView = null;
YField yField = null;
Iterator<EObject> iter = ((EObject) viewContext.getViewEditpart().getModel()).eAllContents();
while (iter.hasNext()) {
EObject eObject = iter.next();
if (eObject instanceof YStrategyLayout) {
YStrategyLayout yLayout = (YStrategyLayout) eObject;
yView = yLayout.getView();
}
if (eObject instanceof YField) {
if (eObject instanceof YFocusable && ((YField) eObject).isVisible()
&& ((YField) eObject).isEditable() && ((YField) eObject).isEnabled()) {
yField = (YField) eObject;
break;
}
}
}
if (yView != null && yField != null) {
yView.setCurrentFocus(yField);
}
}
}
/**
* Creates a new dto.
*
* @param typeName
* the type name for the dto
*/
protected void createNew(String typeName) {
Object newDto = null;
Class<?> clazz = null;
try {
clazz = dslMetadataService.createType(typeName);
} catch (ClassNotFoundException e) {
log.error("{}", e);
return;
}
try {
newDto = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
log.error("{}", e);
return;
}
if (viewContext != null) {
viewContext.setBean(IViewContext.MAIN_BEAN_SLOT, newDto);
}
}
/**
* Sets the input to the dialog.
*
* @param dto
*/
public void setInput(Object dto) {
if (dto == null) {
return;
}
if (viewContext != null) {
DTO persistentDto = dtoService.get(DtoUtils.getIdValue(dto));
viewContext.setBean(IViewContext.MAIN_BEAN_SLOT, persistentDto);
}
}
/**
* If true, then EventDispatcherEvents are being ignored.
*
* @param manualInput
*/
public void setEnableManualInput(boolean manualInput) {
this.manualInput = manualInput;
}
public void createComponents() {
registerPersistenceUnit(persistenceService);
// DTO Services (via DS)
dtoService = getDtoService();
// OSBP UI Code
viewContext = ecviewComponent.setValue(getViewId(), null, renderingParams);
viewContext.setLocale(user.getLocale());
YView yView = (YView) viewContext.getViewEditpart().getModel();
if (yView == null) {
Notification.show("Dialog could not be found!", Type.ERROR_MESSAGE);
return;
}
// initial state of the dialog has to be NEW. This fills the view
// context with a initial blank bean.
createNew(getBaseDtoName());
}
/**
* Removes the old view.
*/
private void removeOldView() {
if (viewContext != null) {
viewContext.dispose();
}
if (layoutManager.getDataArea().getComponentCount() > 0) {
layoutManager.getDataArea().removeAllComponents();
}
}
/**
* Creates the rendering parameters.
*
* @return the map
*/
protected Map<String, Object> createRenderingParams() {
Map<String, Object> params = new HashMap<String, Object>();
Map<String, Object> services = new HashMap<String, Object>();
params.put(IViewContext.PARAM_SERVICES, services);
params.put(IViewContext.PROP_SLOT, IViewContext.MAIN_BEAN_SLOT);
params.put(IViewContext.PARAM_THIRDPARTY_SERVICE_PROVIDER, new ECViewToE4ServiceBridge(eclipseContext));
services.put(org.eclipse.osbp.runtime.common.event.IEventBroker.class.getName(),
new E4EventBrokerAdapter(eventBroker));
services.put(IUser.class.getName(), user);
return params;
}
/**
* Shows a dialog which warns about loosing data.
*
* @param context
* the context
* @param currentDto
* the current dto
* @param newDto
* the new dto
*/
protected void showLoosingDataDialog(IViewContext context, Object currentDto, Object newDto) {
AcceptLoosingDataDialog.showDialog(dslMetadataService, themeResourceService, new Runnable() {
@Override
public void run() {
viewContext.setBean(IViewContext.MAIN_BEAN_SLOT, newDto);
}
}, null);
}
protected void showSaveDialog(Object newDto) {
AcceptSaveDialog.showDialog(dslMetadataService, themeResourceService, new Runnable() {
@Override
public void run() {
viewContext.setBean(IViewContext.MAIN_BEAN_SLOT, newDto);
}
}, null);
}
protected void showDeleteDialog() {
AcceptDeleteDialog.showDialog(dslMetadataService, themeResourceService, new Runnable() {
@Override
public void run() {
createNew(getBaseDtoName());
}
}, null);
}
/**
* Gets the uuid.
*
* @return the uuid
*/
protected String getUUID() {
return uuid;
}
@Override
public void localeChanged(final Locale locale) {
if (viewContext != null) {
viewContext.setLocale(locale);
}
if (layoutManager != null) {
layoutManager.setLabelValue(
dslMetadataService.translate(user.getLocale().toLanguageTag(), getBindingPropertyName()));
}
}
@Override
public void receiveEvent(final EventDispatcherEvent event) {
switch (event.getCommand()) {
case SELECT:
// TODO ask Riegel
if (manualInput) {
return;
}
if (dtoService == null) {
return;
}
MPerspective perspective = eclipseContext.get(MPerspective.class);
ArrayList<String> allowedSenderParts = (ArrayList<String>) eclipseContext.getActive(MPart.class).getTransientData().get(IPerspectiveProvider.EventDispatcherConstants.ACCEPTED_SENDERS);
if((event.getPerspective() == null || (perspective != null && event.getPerspective().equals(perspective))) && ((allowedSenderParts != null && allowedSenderParts.contains(event.getSender())) || allowedSenderParts == null)){
if (!event.getSender().equals(getViewId())
&& (event.getTopic().equals(getBaseDtoName()) || event.getTopic().equals(getPrimaryKey()))) {
Object dto = null;
if (event.getData().containsKey(EventDispatcherDataTag.ID)) {
Object id = event.getData().get(EventDispatcherDataTag.ID);
if (id != null) {
dto = dtoService.get(id);
}
} else if (event.getData().containsKey(EventDispatcherDataTag.DTO)) {
dto = event.getData().get(EventDispatcherDataTag.DTO);
}
if (dto != null) {
updateData(dto);
newCreatedDto = false;
reloadRequestService.setMode(true);
} else {
createNew(getBaseDtoName());
}
}
break;
}
case REFRESH:
if (!event.getSender().equals(getViewId()) && event.getTopic().equals(getBaseEntityName())) {
if (!newCreatedDto) {
DTO currentDto = (DTO) viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
IDto newDto = (IDto) dtoService.get(getIdForDto(currentDto));
updateData(newDto);
}
}
break;
case SAVE:
if (!event.getSender().equals(getViewId()) && event.getTopic().equals(getBaseEntityName())) {
if (event.getData().containsKey(EventDispatcherDataTag.ID)) {
Object eventDataId = event.getData().get(EventDispatcherDataTag.ID);
DTO currentDto = (DTO) viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
Object currentDtoId = getIdForDto(currentDto);
IDto newDto = (IDto) dtoService.get(eventDataId);
if (eventDataId != null && idEquals(eventDataId, currentDtoId)
&& !newDto.equalVersions((IDto) currentDto)) {
showSaveDialog(newDto);
}
}
}
break;
case DELETE:
if (!event.getSender().equals(getViewId()) && event.getTopic().equals(getBaseEntityName())) {
if (event.getData().containsKey(EventDispatcherDataTag.ID)) {
Object eventDataId = event.getData().get(EventDispatcherDataTag.ID);
DTO currentDto = (DTO) viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
Object currentDtoId = getIdForDto(currentDto);
if (eventDataId != null && idEquals(eventDataId, currentDtoId)) {
showDeleteDialog();
}
}
}
break;
}
}
/**
* Subscribe.
*/
protected void subscribe() {
eventBroker.subscribe(EventBrokerMsg.DIALOG_STRATEGY_CHANGED, strategyEventHandler);
}
/**
* Unsubscribe.
*/
protected void unsubscribe() {
eventBroker.unsubscribe(strategyEventHandler);
}
@PostConstruct
public void activate() {
user.addUserLocaleListener(this);
eventDispatcher.addEventReceiver(this);
subscribe();
}
@PreDestroy
public void deactivate() {
user.removeUserLocaleListener(this);
eventDispatcher.removeEventReceiver(this);
unsubscribe();
}
private boolean idEquals(Object id, Object id2) {
if (id2.getClass().isAssignableFrom(id.getClass())) {
if (id instanceof String) {
return ((String) id).equals(id2);
} else if (id instanceof Integer) {
return ((Integer) id).intValue() == ((Integer) id2).intValue();
}
}
return false;
}
/**
* Update data with a new dto and protect dirty data.
*
* @param newDto
* the new dto
*/
private void updateData(Object newDto) {
if (viewContext == null) {
return;
}
Object currentDto = viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
if (currentDto != null) {
boolean dirty = false;
// check if any dto in the dto tree is dirty
DirtyStateAdapter adapter = DtoUtils.getAdapter(DirtyStateAdapter.class, currentDto);
if (adapter != null) {
dirty = adapter.isDirty();
}
if (dirty) {
showLoosingDataDialog(viewContext, currentDto, newDto);
} else if (!((IDto) currentDto).equalVersions(((IDto) newDto))) {
viewContext.setBean(IViewContext.MAIN_BEAN_SLOT, newDto);
}
} else {
viewContext.setBean(IViewContext.MAIN_BEAN_SLOT, newDto);
}
}
/**
* Gets the view id.
*
* @return the view id
*/
protected abstract String getViewId();
/**
* Gets the full qualified dto name.
*
* @return the base class dto name
*/
protected abstract String getBaseDtoName();
/**
* Gets the full qualified entity name.
*
* @return the base class entity name
*/
protected abstract String getBaseEntityName();
/**
* Gets the id for dto.
*
* @param savedDto
* the saved dto
* @return the id for dto
*/
protected abstract Object getIdForDto(DTO savedDto);
/**
* Gets the data name.
*
* @return the data name
*/
protected abstract String getDataName();
/**
* Gets the qualified primary key.
*
* @return the primary key
*/
protected abstract String getPrimaryKey();
/**
* Gets the binding property name.
*
* @return the binding property name
*/
protected abstract String getBindingPropertyName();
/**
* Gets the dto service.
*
* @return the dto service
*/
protected abstract IDTOServiceWithMutablePersistence<DTO> getDtoService();
protected abstract void registerPersistenceUnit(IPersistenceService persistenceService);
/**
* Creates the toobar helper.
*
* @return the i dialog toolbar action
*/
protected abstract IToolbarAction createToobarHelper();
/**
* Gets the num columns to be layouted.
*
* @return the num columns
*/
protected abstract int getNumColumns();
@Override
public String getStateLabelUUID() {
return null;
}
}