blob: d49d41ba6a8375ef1e67dcbd4dd7ad3ba3a26ab5 [file] [log] [blame]
/**
* Copyright (c) 2011, 2015 - Lunifera GmbH (Gross Enzersdorf, Austria), 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:
* Florian Pirchner - Initial implementation
*/
package org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.internal;
import java.util.Locale;
import java.util.Map;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.emf.databinding.EMFObservables;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.osbp.ecview.core.common.beans.InMemoryBeanProvider;
import org.eclipse.osbp.ecview.core.common.context.ContextException;
import org.eclipse.osbp.ecview.core.common.context.IDialogViewContextCallback;
import org.eclipse.osbp.ecview.core.common.context.IDtoServiceCallback;
import org.eclipse.osbp.ecview.core.common.context.IViewContext;
import org.eclipse.osbp.ecview.core.common.editpart.IElementEditpart;
import org.eclipse.osbp.ecview.core.common.model.core.CoreModelFactory;
import org.eclipse.osbp.ecview.core.common.model.core.YAlignment;
import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddable;
import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddableBindingEndpoint;
import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddableValueEndpoint;
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.extension.model.extension.ExtensionModelFactory;
import org.eclipse.osbp.ecview.core.extension.model.extension.ExtensionModelPackage;
import org.eclipse.osbp.ecview.core.extension.model.extension.YBeanReferenceField;
import org.eclipse.osbp.ecview.core.extension.model.extension.YVerticalLayout;
import org.eclipse.osbp.ecview.core.extension.model.extension.YVerticalLayoutCellStyle;
import org.eclipse.osbp.ecview.core.ui.core.editparts.extension.IBeanReferenceFieldEditpart;
import org.eclipse.osbp.runtime.common.annotations.TargetEnumConstraints;
import org.eclipse.osbp.runtime.common.state.ISharedStateContext;
import org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.VaadinRenderer;
import org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.AbstractFieldWidgetPresenter;
import org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.internal.util.Util;
import org.eclipse.osbp.runtime.web.vaadin.common.data.IBeanSearchService;
import org.eclipse.osbp.runtime.web.vaadin.common.data.IBeanSearchServiceFactory;
import org.eclipse.osbp.runtime.web.vaadin.common.data.StatefulInMemoryBeanSearchService;
import org.eclipse.osbp.runtime.web.vaadin.common.services.filter.AnnotationToVaadinFilterConverter;
import org.eclipse.osbp.runtime.web.vaadin.components.fields.BeanReferenceField;
import org.eclipse.osbp.ui.api.metadata.IDSLMetadataService;
import org.eclipse.osbp.ui.api.user.IUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.data.Container.Filter;
import com.vaadin.server.ClientConnector.DetachEvent;
import com.vaadin.server.ClientConnector.DetachListener;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.Resource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.ui.Field;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
/**
* This presenter is responsible to render a text field on the given layout.
*/
public class BeanReferenceFieldPresentation extends AbstractFieldWidgetPresenter<Component> {
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(BeanReferenceFieldPresentation.class);
/** The model access. */
private final ModelAccess modelAccess;
/** The field. */
private CustomField<Object> field;
private DialogHandleImpl filterHandle;
private DialogHandleImpl dialogHandle;
private IDSLMetadataService dslMetadataService;
private IUser user;
/**
* Constructor.
*
* @param editpart
* The editpart of that presenter
*/
public BeanReferenceFieldPresentation(IElementEditpart editpart) {
super((IBeanReferenceFieldEditpart) editpart);
this.modelAccess = new ModelAccess((YBeanReferenceField) editpart.getModel());
}
/**
* {@inheritDoc}
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Component doCreateWidget(Object parent) {
if (field == null) {
IBeanSearchServiceFactory searchServiceFactory = getViewContext()
.getService(IBeanSearchServiceFactory.class.getName());
IBeanSearchService<?> service = getSearchService(searchServiceFactory);
ISharedStateContext sharedState = getViewContext().getService(ISharedStateContext.class.getName());
dslMetadataService = getViewContext().getService(IDSLMetadataService.class.getName());
user = getViewContext().getService(IUser.class.getName());
field = new CustomField(getEditpart().getId(), "", modelAccess.yField.getType(), service, createFilter(),
sharedState);
field.addStyleName(CSS_CLASS_CONTROL);
field.setNullRepresentation("");
field.setImmediate(true);
setupComponent(field, getCastedModel());
String captionPropertyPath = modelAccess.yField.getCaptionPropertyPath();
if (captionPropertyPath == null || captionPropertyPath.isEmpty()) {
captionPropertyPath = modelAccess.yField.getDescriptionProperty();
}
field.setItemCaptionPropertyId(captionPropertyPath);
field.setItemIconPropertyId(modelAccess.yField.getImagePropertyPath());
associateWidget(field, modelAccess.yField);
if (modelAccess.isCssIdValid()) {
field.setId(modelAccess.getCssID());
} else {
field.setId(getEditpart().getId());
}
field.setNullSelectionAllowed(!modelAccess.yField.isRequired());
// creates the binding for the field
createBindings(modelAccess.yField, field);
if (modelAccess.isCssClassValid()) {
field.addStyleName(modelAccess.getCssClass());
}
if (modelAccess.yField.getFilteringComponent() != null) {
filterHandle = new DialogHandleImpl();
field.setFilterHandle(filterHandle);
field.setFilterDialog(true);
}
if (modelAccess.yField.getDialogComponent() != null) {
dialogHandle = new DialogHandleImpl();
field.setDialogHandle(dialogHandle);
field.setDialog(true);
}
applyCaptions();
initializeField(field);
}
return field;
}
/**
* Creates a vaadin filter for the field annotations.
*
* @return the filter
*/
protected Filter createFilter() {
Class<?> sourceType = modelAccess.yField.getReferenceSourceType();
String sourceProperty = modelAccess.yField.getReferenceSourceTypeProperty();
if (sourceType == null || sourceProperty == null || sourceProperty.equals("")) {
return null;
}
try {
java.lang.reflect.Field sourceField = sourceType.getDeclaredField(sourceProperty);
TargetEnumConstraints annotation = sourceField.getAnnotation(TargetEnumConstraints.class);
if (annotation != null) {
return AnnotationToVaadinFilterConverter.createFilter(annotation);
}
} catch (Exception e) {
LOGGER.warn("{}", e);
return null;
}
return null;
}
/**
* Returns the bean search service.
*
* @param searchServiceFactory
* the search service factory
* @return the search service
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected IBeanSearchService<?> getSearchService(IBeanSearchServiceFactory searchServiceFactory) {
IBeanSearchService<?> service = null;
Class<? extends InMemoryBeanProvider> inMemoryProviderClass = (Class<? extends InMemoryBeanProvider>) modelAccess.yField
.getInMemoryBeanProvider();
if (inMemoryProviderClass != null) {
try {
InMemoryBeanProvider provider = inMemoryProviderClass.newInstance();
StatefulInMemoryBeanSearchService tempService = new StatefulInMemoryBeanSearchService(
modelAccess.yField.getType());
tempService.addBeans(provider.getBeans());
service = tempService;
} catch (InstantiationException e) {
LOGGER.error("{}", e);
} catch (IllegalAccessException e) {
LOGGER.error("{}", e);
}
}
if (service == null) {
service = searchServiceFactory.createService(modelAccess.yField.getType());
}
return service;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.
* AbstractVaadinWidgetPresenter#doUpdateLocale(java.util.Locale)
*/
@Override
protected void doUpdateLocale(Locale locale) {
// no need to set the locale to the ui elements. Is handled by vaadin
// internally.
// update the captions
applyCaptions();
}
/**
* Applies the labels to the widgets.
*/
protected void applyCaptions() {
Util.applyCaptions(getI18nService(), modelAccess.getLabel(), modelAccess.getLabelI18nKey(), getLocale(), field);
Util.applyCaptionIcons(getI18nService(), getResourceProvider(), modelAccess.getLabelI18nKey(), getLocale(),
new Util.ResourceCallback() {
@Override
public void setIcon(Resource resource) {
field.setSearchButtonIcon(resource);
}
});
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.
* AbstractFieldWidgetPresenter#doGetField()
*/
@Override
protected Field<?> doGetField() {
return field;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.common.
* AbstractVaadinWidgetPresenter
* #internalGetObservableEndpoint(org.eclipse.osbp
* .ecview.core.common.model.core.YEmbeddableBindingEndpoint)
*/
@Override
protected IObservable internalGetObservableEndpoint(YEmbeddableBindingEndpoint bindableValue) {
if (bindableValue == null) {
throw new IllegalArgumentException("BindableValue must not be null!");
}
if (bindableValue instanceof YEmbeddableValueEndpoint) {
return internalGetValueEndpoint();
}
throw new IllegalArgumentException("Not a valid input: " + bindableValue);
}
/**
* Returns the observable to observe value.
*
* @return the i observable value
*/
@SuppressWarnings("rawtypes")
protected IObservableValue internalGetValueEndpoint() {
// return the observable value for text
return EMFObservables.observeValue(castEObject(getModel()),
ExtensionModelPackage.Literals.YBEAN_REFERENCE_FIELD__VALUE);
}
/**
* Creates the bindings for the given values.
*
* @param yField
* the y field
* @param field
* the field
*/
protected void createBindings(YBeanReferenceField yField, BeanReferenceField<?> field) {
// create the model binding from ridget to ECView-model
registerBinding(createBindings_Value(castEObject(getModel()),
ExtensionModelPackage.Literals.YBEAN_REFERENCE_FIELD__VALUE, field));
super.createBindings(yField, field, null);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.ecview.core.common.presentation.IWidgetPresentation#
* getWidget()
*/
@Override
public Component getWidget() {
return field;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.ecview.core.common.presentation.IWidgetPresentation#
* isRendered()
*/
@Override
public boolean isRendered() {
return field != null;
}
/**
* {@inheritDoc}
*/
@Override
public void doUnrender() {
if (field != null) {
// unbind all active bindings
unbind();
Component parent = ((Component) field.getParent());
if (parent != null && parent instanceof ComponentContainer) {
((ComponentContainer) parent).removeComponent(field);
}
// remove assocations
unassociateWidget(field);
if (field.getInternalComboBox() != null) {
unassociateWidget(field.getInternalComboBox());
}
field = null;
filterHandle = null;
dialogHandle = null;
}
}
/**
* {@inheritDoc}
*/
@Override
protected void internalDispose() {
try {
unrender();
} finally {
super.internalDispose();
}
}
/**
* A helper class.
*/
private static class ModelAccess {
/** The y field. */
private final YBeanReferenceField yField;
/**
* Instantiates a new model access.
*
* @param yField
* the y field
*/
public ModelAccess(YBeanReferenceField yField) {
super();
this.yField = yField;
}
/**
* Gets the css class.
*
* @return the css class
* @see org.eclipse.osbp.ecview.core.ui.core.model.core.YCssAble#getCssClass()
*/
public String getCssClass() {
return yField.getCssClass();
}
/**
* Returns true, if the css class is not null and not empty.
*
* @return true, if is css class valid
*/
public boolean isCssClassValid() {
return getCssClass() != null && !getCssClass().equals("");
}
/**
* Gets the css id.
*
* @return the css id
* @see org.eclipse.osbp.ecview.core.ui.core.model.core.YCssAble#getCssID()
*/
public String getCssID() {
return yField.getCssID();
}
/**
* Returns true, if the css id is not null and not empty.
*
* @return true, if is css id valid
*/
public boolean isCssIdValid() {
return getCssID() != null && !getCssID().equals("");
}
/**
* Returns the label.
*
* @return the label
*/
public String getLabel() {
return yField.getDatadescription() != null ? yField.getDatadescription().getLabel() : null;
}
/**
* Returns the label.
*
* @return the label i18n key
*/
public String getLabelI18nKey() {
return yField.getDatadescription() != null ? yField.getDatadescription().getLabelI18nKey() : null;
}
}
/**
* The Class CustomField.
*
* @param <M>
* the generic type
*/
@SuppressWarnings("serial")
private class CustomField<M> extends BeanReferenceField<M> implements Component.Focusable {
/**
* Instantiates a new custom field.
*
* @param id
* the id
* @param propertyId
* the property id
* @param type
* the type
* @param searchService
* the search service
* @param filter
* the filter
* @param sharedState
* the shared state
*/
public CustomField(String id, Object propertyId, Class<M> type, IBeanSearchService<M> searchService,
Filter filter, ISharedStateContext sharedState) {
super(id, propertyId, type, searchService, filter, sharedState);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.runtime.web.vaadin.components.fields.
* BeanReferenceField #initContent()
*/
@Override
protected Component initContent() {
Component result = super.initContent();
associateWidget(field.getInternalComboBox(), getCastedModel());
return result;
}
/*
* (non-Javadoc)
*
* @see com.vaadin.ui.AbstractField#getErrorMessage()
*/
@Override
public ErrorMessage getErrorMessage() {
if (isDisposed()) {
// after disposal, Vaadin will call this method once.
return null;
}
ErrorMessage message = super.getErrorMessage();
reportValidationError(message);
return message;
}
}
@SuppressWarnings("serial")
class DialogHandleImpl implements BeanReferenceField.DialogHandle, DetachListener {
CoreModelFactory coreModelFactory = CoreModelFactory.eINSTANCE;
ExtensionModelFactory modelFactory = ExtensionModelFactory.eINSTANCE;
private IViewContext dialogViewContext;
private Window window;
private IReloadRequestService reloadRequestService = new AbstractReloadRequestService() {
@Override
public void requestReload(YEmbeddable component, Object newDto) {
dialogViewContext.setBean(getBeanSlotName(dialogViewContext.getRenderingParams()), newDto); }
};
@SuppressWarnings("unchecked")
@Override
public void open() {
window = new Window();
window.setClosable(true);
window.setModal(true);
window.addDetachListener(this);
VerticalLayout content = new VerticalLayout();
content.setMargin(true);
content.setSpacing(true);
content.setSizeFull();
window.setContent(content);
VerticalLayout renderTarget = new VerticalLayout();
renderTarget.setMargin(true);
renderTarget.setSpacing(true);
renderTarget.setSizeFull();
content.addComponent(renderTarget);
content.setExpandRatio(renderTarget, 1.0f);
UI ui = BeanReferenceFieldPresentation.this.field.getUI();
ui.addWindow(window);
VaadinRenderer renderer = new VaadinRenderer();
if(modelAccess.yField.getDialogComponent() != null && modelAccess.yField.getDialogComponent().getViewContextCallback() != null) {
reloadRequestService.setMode(true);
Class<?> dtoType = modelAccess.yField.getDialogComponent().getType();
IDialogViewContextCallback callback = (IDialogViewContextCallback) modelAccess.yField.getDialogComponent().getViewContextCallback();
dialogViewContext = callback.getDialogViewContext(dtoType);
((Map<String,Object>)dialogViewContext.getRenderingParams().get(IViewContext.PARAM_SERVICES)).put(IReloadRequestService.class.getName(), reloadRequestService);
if(!dialogViewContext.isRendered()) {
try {
renderer.render(dialogViewContext, renderTarget, dialogViewContext.getRenderingParams());
} catch (ContextException e) {
LOGGER.error("{}", e);
}
}
HorizontalLayout buttonArea = new HorizontalLayout();
content.addComponent(buttonArea);
content.setComponentAlignment(buttonArea, Alignment.BOTTOM_RIGHT);
Button add = new Button(dslMetadataService.translate(user.getLocale().toLanguageTag(), "update"), e -> {
IDtoServiceCallback updateCallback = (IDtoServiceCallback)modelAccess.yField.getDialogComponent().getUpdateCallback();
if(updateCallback != null) {
updateCallback.persist(dialogViewContext);
}
close();
});
buttonArea.addComponent(add);
Button cancel = new Button(dslMetadataService.translate(user.getLocale().toLanguageTag(), "cancel"), e -> {
close();
});
buttonArea.addComponent(cancel);
} else if(modelAccess.yField.getFilteringComponent() != null) {
YView yView = getViewModel();
try {
dialogViewContext = renderer.render(renderTarget, yView, null);
} catch (ContextException e) {
LOGGER.error("{}", e);
}
Button accept = new Button(dslMetadataService.translate(user.getLocale().toLanguageTag(), "accept"), e -> {
close();
if(modelAccess.yField.getFilteringComponent() != null) {
field.setValue(dialogViewContext.getBean(modelAccess.yField.getFilteringComponent().getSelectionBeanSlotName()));
}
});
content.addComponent(accept);
content.setComponentAlignment(accept, Alignment.BOTTOM_RIGHT);
dialogViewContext.setLocale(user.getLocale());
}
window.center();
}
protected YView getViewModel() {
YView yView = coreModelFactory.createYView();
YVerticalLayout yContent = modelFactory.createYVerticalLayout();
yView.setContent(yContent);
YEmbeddable comp = null;
yContent.getElements().add(EcoreUtil.copy(modelAccess.yField.getFilteringComponent()));
YVerticalLayoutCellStyle cellStyle = yContent.addCellStyle(comp);
cellStyle.setAlignment(YAlignment.FILL_FILL);
return yView;
}
@Override
public void close() {
window.close();
window.removeDetachListener(this);
}
@Override
public void detach(DetachEvent event) {
if(dialogViewContext != null && !dialogViewContext.isDisposed()) {
dialogViewContext.dispose();
dialogViewContext = null;
}
}
protected String getBeanSlotName(Map<String, Object> properties) {
return (String) properties.get(IViewContext.PROP_SLOT);
}
}
}