| /** |
| * 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.common.types.IBundleSpace; |
| 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() { |
| String sourceTypeFqn = modelAccess.yField.getReferenceSourceTypeQualifiedName(); |
| String sourceProperty = modelAccess.yField.getReferenceSourceTypeProperty(); |
| if (sourceTypeFqn == null || sourceProperty == null || sourceProperty.equals("")) { |
| return null; |
| } |
| |
| IBundleSpace bundleSpace = getViewContext().getService(IBundleSpace.class.getName()); |
| Class<?> sourceType = null; |
| try { |
| sourceType = bundleSpace.forName(sourceTypeFqn); |
| } catch (ClassNotFoundException e1) { |
| LOGGER.error("{}", e1); |
| } |
| |
| 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); |
| } |
| } |
| } |