blob: dbe643c3d3ee5c9ed8ddbbaf85d82f54ff935e67 [file] [log] [blame]
/**
* Copyright (c) 2011, 2014 - 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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florian Pirchner - Initial implementation
*/
package org.eclipse.osbp.vaaclipse.addons.ecview.views;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
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.ui.di.Persist;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.commands.MCommand;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.menu.MHandledToolItem;
import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
import org.eclipse.e4.ui.model.application.ui.menu.MToolItem;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.osbp.dsl.dto.lib.services.DtoServiceException;
import org.eclipse.osbp.dsl.dto.lib.services.IDTOService;
import org.eclipse.osbp.ecview.core.common.beans.ISlot;
import org.eclipse.osbp.ecview.core.common.context.ContextException;
import org.eclipse.osbp.ecview.core.common.context.IViewContext;
import org.eclipse.osbp.ecview.core.common.editpart.IFieldEditpart;
import org.eclipse.osbp.ecview.core.common.model.core.CoreModelPackage;
import org.eclipse.osbp.ecview.core.common.model.core.YBeanSlot;
import org.eclipse.osbp.ecview.core.common.model.core.YEmbeddable;
import org.eclipse.osbp.ecview.core.common.model.core.YExposedAction;
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.model.core.util.BindingIdUtil;
import org.eclipse.osbp.ecview.core.common.validation.IFieldValidationManager;
import org.eclipse.osbp.ecview.core.util.emf.ModelUtil;
import org.eclipse.osbp.runtime.common.annotations.DtoUtils;
import org.eclipse.osbp.runtime.common.event.IEventBroker;
import org.eclipse.osbp.runtime.common.i18n.II18nService;
import org.eclipse.osbp.runtime.common.state.ISharedStateContext;
import org.eclipse.osbp.runtime.common.state.SharedStateUnitOfWork;
import org.eclipse.osbp.runtime.common.validation.IStatus;
import org.eclipse.osbp.runtime.common.validation.ValidationKind;
import org.eclipse.osbp.runtime.web.ecview.presentation.vaadin.VaadinRenderer;
import org.eclipse.osbp.runtime.web.vaadin.common.resource.IResourceProvider;
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.AcceptReloadDialog;
import org.eclipse.osbp.vaaclipse.addons.common.api.IE4Constants;
import org.eclipse.osbp.vaaclipse.addons.common.api.IE4Topics;
import org.eclipse.osbp.vaaclipse.addons.common.api.di.Callback;
import org.eclipse.osbp.vaaclipse.addons.common.api.di.Delete;
import org.eclipse.osbp.vaaclipse.addons.common.api.di.EnabledState;
import org.eclipse.osbp.vaaclipse.addons.common.api.di.Load;
import org.eclipse.osbp.vaaclipse.addons.common.api.di.Validate;
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.addons.common.event.EventTopicNormalizer;
import org.eclipse.osbp.vaaclipse.addons.ecview.event.E4EventBrokerAdapter;
import org.eclipse.osbp.vaaclipse.addons.keybinding.IKeyBindingService;
import org.eclipse.osbp.vaaclipse.publicapi.commands.IPartItemExecutionService;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.event.LayoutEvents;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Notification.Type;
import com.vaadin.ui.VerticalLayout;
/**
* A generic ECView view part implementation. It will be used by the
* DynamicViewSupport to create views on the fly.
* <p>
* Client should not subclass this implementation.
*/
public class GenericECViewPart implements IFieldValidationManager.Listener {
/** The Constant BEAN_SLOT__VALIDATION_ERROR. */
private static final String BEAN_SLOT__VALIDATION_ERROR = "validationError";
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory
.getLogger(GenericECViewPart.class);
/** The eclipse context. */
@Inject
private IEclipseContext eclipseContext;
/** The m part. */
@Inject
private MPart mPart;
/** The parent layout. */
@Inject
private VerticalLayout parentLayout;
/** The e4 event broker. */
@Inject
private org.eclipse.e4.core.services.events.IEventBroker e4EventBroker;
/** The topic normalizer. */
@Inject
private EventTopicNormalizer topicNormalizer;
/** The i18n service. */
@Inject
private II18nService i18nService;
/** The resource provider. */
@Inject
private IResourceProvider resourceProvider;
/** The y view. */
@Inject
private YView yView;
/** The shared state. */
@Inject
private ISharedStateContext sharedState;
/** The dto service. */
@Inject
private IDTOService<Object> dtoService;
/** The part service. */
@Inject
private EPartService partService;
/** The status manager. */
@Inject
private IStatusManager statusManager;
/** The key binding service. */
@Inject
private IKeyBindingService keyBindingService;
/** The command info. */
private Map<String, CommandInfo> commandInfo = new HashMap<String, CommandInfo>();
/** The exposed actions callback. */
private ExposedActionsCallback exposedActionsCallback;
/** The view context. */
private IViewContext viewContext;
/** The redirected eventtopics. */
private HashMap<String, Set<YBeanSlot>> redirectedEventtopics;
/** The event handlers. */
private Set<EventHandler> eventHandlers = new HashSet<EventHandler>();
/** The ecview field validation manager. */
private IFieldValidationManager ecviewFieldValidationManager;
/** The validation result. */
private Set<IStatus> validationResult;
/**
* Instantiates a new generic ec view part.
*/
public GenericECViewPart() {
}
/**
* Setup.
*/
@SuppressWarnings("serial")
@PostConstruct
public void setup() {
if (yView == null) {
Notification.show("View model is not available!",
Notification.Type.ERROR_MESSAGE);
return;
}
exposedActionsCallback = new ExposedActionsCallback();
VerticalLayout layout = new VerticalLayout();
parentLayout.addComponent(layout);
layout.setSizeFull();
redirectEventTopics(yView);
// render the Vaadin UI
Map<String, Object> properties = new HashMap<String, Object>();
// register services to be used
Map<String, Object> services = new HashMap<String, Object>();
services.put(org.eclipse.osbp.runtime.common.event.IEventBroker.class
.getName(), new E4EventBrokerAdapter(e4EventBroker));
if (sharedState != null) {
services.put(ISharedStateContext.class.getName(), sharedState);
}
properties.put(IViewContext.PARAM_SERVICES, services);
VaadinRenderer renderer = new VaadinRenderer();
try {
viewContext = renderer.render(layout, yView, properties);
viewContext.createBeanSlot(BEAN_SLOT__VALIDATION_ERROR,
Boolean.class);
viewContext.setBean(BEAN_SLOT__VALIDATION_ERROR, Boolean.FALSE);
} catch (ContextException e) {
LOGGER.error("{}", e);
}
// bridges the validation from ECView core to Vaaclipse
setupValidationBridge();
// register a layout click listener to activate the part.
layout.addLayoutClickListener(new LayoutEvents.LayoutClickListener() {
@Override
public void layoutClick(LayoutClickEvent event) {
partService.activate(mPart);
}
});
setupFocusFieldFeature();
preparePartToolbar(yView);
}
/**
* Setup the focus-field feature. Handles events from event broker and tries
* to focus the element.
*/
protected void setupFocusFieldFeature() {
// install an event handler that is responsible to focus elements
EventHandler focusEventHandler = new EventHandler() {
@Override
public void handleEvent(Event event) {
String partId = (String) event
.getProperty(IE4Topics.PartEvents.PROP_MPART_ID);
if (partId != null && partId.equals(mPart.getElementId())) {
String fieldId = (String) event
.getProperty(IE4Topics.PartEvents.PROP_FIELD_ID);
if (fieldId != null && !fieldId.equals("")) {
YEmbeddable yElement = (YEmbeddable) viewContext
.findModelElement(fieldId);
if (yElement instanceof YFocusable) {
YView yView = (YView) viewContext.getViewEditpart()
.getModel();
yView.setCurrentFocus((YFocusable) yElement);
}
}
}
}
};
eventHandlers.add(focusEventHandler);
e4EventBroker.subscribe(IE4Topics.PartEvents.FOCUS_FIELD_TOPIC,
focusEventHandler);
}
/**
* Setup validation bridge.
*/
protected void setupValidationBridge() {
// create a validation enhancer. If an error message is added to the
// field validation manager. The enhancer will set the id of the mpart
// to the status.
ecviewFieldValidationManager = viewContext
.getService(IFieldValidationManager.class.getName());
ecviewFieldValidationManager
.setEnhancer(new IFieldValidationManager.Enhancer() {
@Override
public void enhance(IStatus status) {
status.putProperty(IStatus.PROP_UI_APPLICATION_ID,
mPart.getElementId());
}
});
ecviewFieldValidationManager.addListener(this);
}
/* (non-Javadoc)
* @see org.eclipse.osbp.ecview.core.common.validation.IFieldValidationManager.Listener#validationChanged(org.eclipse.osbp.ecview.core.common.validation.IFieldValidationManager.Event)
*/
@Override
public void validationChanged(IFieldValidationManager.Event event) {
// add them to the Vaaclipse status manager
IStatusScope scope = statusManager.getScopeFor(mPart);
scope.modifyStatus(event.getOldStatus(), event.getNewStatus());
applyValidationResult();
}
/**
* Remove defined event topics at bean slots. We pass them to custom
* handlers which will forward to the view. So we can avoid loosing changed
* data.
*
* @param yView
* the y view
*/
private void redirectEventTopics(YView yView) {
// redirect event topics
redirectedEventtopics = new HashMap<String, Set<YBeanSlot>>();
for (YBeanSlot yBeanSlot : yView.getBeanSlots()) {
String eventTopic = yBeanSlot.getEventTopic();
if (eventTopic != null && !eventTopic.trim().equals("")) {
eventTopic = eventTopic.trim();
// events will be forwarded by this view
yBeanSlot.setRedirectEvents(true);
Set<YBeanSlot> yRedirSlots = redirectedEventtopics
.get(eventTopic);
if (yRedirSlots == null) {
yRedirSlots = new HashSet<YBeanSlot>();
redirectedEventtopics.put(eventTopic, yRedirSlots);
}
yRedirSlots.add(yBeanSlot);
}
}
if (redirectedEventtopics.size() > 0) {
// install event handler for each redirected topic
for (String eventTopic : redirectedEventtopics.keySet()) {
// install the event handler
EventHandler handler = new EventHandler() {
@Override
public void handleEvent(Event event) {
dispatchEventBrokerEvent(event);
}
};
e4EventBroker.subscribe(eventTopic, handler);
eventHandlers.add(handler);
}
}
}
/**
* Save.
*
* @param commandId
* the command id
*/
@Persist
public void save(@Named("commandId") String commandId) {
final Object mainDto = viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
boolean processedProperly = false;
try {
if (mainDto != null) {
new SharedStateUnitOfWork<Object>() {
@Override
protected Object doExecute() {
dtoService.update(mainDto);
return null;
}
}.execute(sharedState);
// in case of exception, it is still false
processedProperly = true;
} else {
}
} finally {
if (processedProperly) {
notifyExecuted(toExposedAction(commandId));
} else {
notifyCanceled(toExposedAction(commandId));
}
}
}
/**
* To exposed action.
*
* @param commandId
* the command id
* @return the y exposed action
*/
private YExposedAction toExposedAction(String commandId) {
return commandInfo.get(commandId).action;
}
/**
* Validate.
*
* @param commandId
* the command id
*/
@Validate
public void validate(@Named("commandId") String commandId) {
if (commandId != null) {
Set<IStatus> oldValidationResult = validationResult != null ? new HashSet<IStatus>(
validationResult) : new HashSet<IStatus>();
final Object mainDto = viewContext
.getBean(IViewContext.MAIN_BEAN_SLOT);
validationResult = dtoService.validate(mainDto,
ValidationKind.OTHER, null);
for (IStatus status : validationResult) {
// set the application id to the status
status.putProperty(IStatus.PROP_UI_APPLICATION_ID,
mPart.getElementId());
if (status.containsProperty(IStatus.PROP_FIELD_ID)
&& status.containsProperty(IStatus.PROP_FIELD_I18N_KEY)) {
continue;
}
// fix the field id. Therefore we try to find the yField that is
// bound to the property path of javax.validation result.
if (!status.containsProperty(IStatus.PROP_FIELD_ID)) {
if (status
.containsProperty(IStatus.PROP_JAVAX_PROPERTY_PATH)) {
String path = (String) status
.getProperty(IStatus.PROP_JAVAX_PROPERTY_PATH);
// if path points to a collection, then remove all the
// collection stuff, since Vaadin can not focus rows
if (path.matches(".*\\[\\d*\\].*")) {
path = path.substring(0, path.indexOf("["));
}
// using a regex to find the yElement:
// "beanslot/.*/{propertyPath}"
YEmbeddable yElement = (YEmbeddable) viewContext
.findBoundField(String.format("%s.*/%s",
BindingIdUtil.BEANSLOT, path));
if (yElement != null) {
status.putProperty(IStatus.PROP_FIELD_ID,
yElement.getId());
status.putProperty(IStatus.PROP_FIELD_I18N_KEY,
yElement.getLabelI18nKey());
}
}
}
}
// now update the field with the new Status instances
//
for (IStatus status : oldValidationResult) {
String fieldId = (String) status
.getProperty(IStatus.PROP_FIELD_ID);
if (fieldId != null && !fieldId.trim().equals("")) {
YField yField = (YField) viewContext
.findModelElement(fieldId);
IFieldEditpart editpart = ModelUtil.findEditpart(yField);
if (editpart != null) {
editpart.removeExternalStatus(status);
}
}
}
for (IStatus status : validationResult) {
String fieldId = (String) status
.getProperty(IStatus.PROP_FIELD_ID);
if (fieldId != null && !fieldId.trim().equals("")) {
YField yField = (YField) viewContext
.findModelElement(fieldId);
IFieldEditpart editpart = ModelUtil.findEditpart(yField);
if (editpart != null) {
editpart.addExternalStatus(status);
}
}
}
statusManager.getScopeFor(mPart).modifyStatus(oldValidationResult,
validationResult);
notifyExternalClicked(toExposedAction(commandId));
applyValidationResult();
}
}
/**
* 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.getScopeFor(mPart).getAllStatus()) {
if (status.isError()) {
viewContext.setBean(BEAN_SLOT__VALIDATION_ERROR, Boolean.TRUE);
break;
}
}
}
/**
* Gets the enabled state.
*
* @param commandId
* the command id
* @return the enabled state
*/
@EnabledState
public boolean getEnabledState(@Named("commandId") String commandId) {
CommandInfo info = findCommandInfo(commandId);
if (info == null) {
return true;
}
return info.enabled;
}
/**
* Delete.
*
* @param commandId
* the command id
*/
@Delete
public void delete(@Named("commandId") final String commandId) {
final Object mainDto = viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
if (mainDto != null) {
AcceptDeleteDialog.showDialog(i18nService, resourceProvider,
new Runnable() {
@Override
public void run() {
new SharedStateUnitOfWork<Object>() {
@Override
protected Object doExecute() {
boolean processed = false;
try {
try {
dtoService.delete(mainDto);
// in case of exception, it is not
// changed
processed = true;
} catch (DtoServiceException e) {
// catch exception, show message and
// return
Notification.show("",
e.getMessage(),
Type.ERROR_MESSAGE);
}
} finally {
if (processed) {
notifyExecuted(toExposedAction(commandId));
} else {
notifyCanceled(toExposedAction(commandId));
}
}
return null;
}
}.execute(sharedState);
}
}, new ActionCanceledAdapter(toExposedAction(commandId)));
} else {
notifyCanceled(toExposedAction(commandId));
}
}
/**
* Notifies the action about cancel.
*
* @param yAction
* the y action
*/
protected void notifyCanceled(final YExposedAction yAction) {
ActionCanceledAdapter.notify(yAction);
}
/**
* Notifies the action about executed.
*
* @param yAction
* the y action
*/
protected void notifyExecuted(final YExposedAction yAction) {
ActionExecutedAdapter.notify(yAction);
}
/**
* Notifies the action about external clicked.
*
* @param yAction
* the y action
*/
protected void notifyExternalClicked(final YExposedAction yAction) {
ActionExternalClickedAdapter.notify(yAction);
}
/**
* Reload.
*
* @param commandId
* the command id
*/
@Load
public void reload(@Named("commandId") final String commandId) {
final Object mainDto = viewContext.getBean(IViewContext.MAIN_BEAN_SLOT);
if (mainDto != null) {
boolean isDirty = false;
try {
isDirty = DtoUtils.isDirty(mainDto);
} catch (IllegalAccessException e) {
// nothing to do
}
// if there is no dirty indicator, or the record is not dirty,
// reload the data
if (!isDirty) {
new SharedStateUnitOfWork<Object>() {
@Override
protected Object doExecute() {
boolean processed = false;
try {
dtoService.reload(mainDto);
// in case of exception, it is not changed
processed = true;
} finally {
if (processed) {
statusManager.getScopeFor(mPart).clearStatus();
notifyExecuted(toExposedAction(commandId));
} else {
notifyCanceled(toExposedAction(commandId));
}
}
return null;
}
}.execute(sharedState);
} else {
AcceptReloadDialog.showDialog(
i18nService,
resourceProvider,
new Runnable() {
@Override
public void run() {
new SharedStateUnitOfWork<Object>() {
@Override
protected Object doExecute() {
boolean processed = false;
try {
dtoService.reload(mainDto);
// in case of exception, it is not
// changed
processed = true;
} finally {
if (processed) {
statusManager
.getScopeFor(mPart)
.clearStatus();
notifyExecuted(toExposedAction(commandId));
} else {
notifyCanceled(toExposedAction(commandId));
}
}
return null;
}
}.execute(sharedState);
}
},
new ActionCanceledAdapter(toExposedAction(commandId)));
}
} else {
notifyCanceled(toExposedAction(commandId));
}
}
/**
* If a command was executed, the original actionId from the ECView view
* (YView) will be passed here.
*
* @param yAction
* the y action
*/
@Callback
public void commandExecuted(YExposedAction yAction) {
if (yAction != null) {
// we are going to forward the execution of the action to the ecView
// exposed action
// final YExposedAction yAction = (YExposedAction) action;
yAction.setExternalClickTime(new Date().getTime());
// check if dto is dirty
//
boolean isDirty = false;
final Object mainDto = viewContext
.getBean(IViewContext.MAIN_BEAN_SLOT);
if (mainDto != null) {
try {
isDirty = DtoUtils.isDirty(mainDto);
} catch (IllegalAccessException e) {
// nothing to do -> if there is not dirty flag, we ignore
// this
}
}
// to notify about executed or canceled state, first check if the
// state is dirty
if (isDirty && yAction.isCheckDirty()) {
// show accept dialog
AcceptLoosingDataDialog.showDialog(i18nService,
resourceProvider, new ActionExecutedAdapter(yAction),
new ActionCanceledAdapter(yAction));
} else {
notifyExecuted(yAction);
}
}
}
/**
* Prepares the toolbar for the view.
*
* @param yView
* the y view
*/
private void preparePartToolbar(YView yView) {
MToolBar mToolbar = mPart.getToolbar();
clearToolbar(mToolbar);
for (YExposedAction yAction : yView.getExposedActions()) {
// register the exposed actions callback to handle enabled state
yAction.eAdapters().add(exposedActionsCallback);
MHandledToolItem toolItem = null;
if (yAction.getExternalCommandId() == null) {
toolItem = createToolItem(yAction,
IE4Constants.COMMAND_DEFAULT_PART_CALLBACK,
yAction.isInitialEnabled());
} else if (yAction.getExternalCommandId() != null) {
toolItem = createToolItem(yAction,
yAction.getExternalCommandId(),
yAction.isInitialEnabled());
}
if (toolItem != null) {
mToolbar.getChildren().add(toolItem);
// set default after rendering
toolItem.setEnabled(yAction.isInitialEnabled());
}
}
}
/**
* Clear toolbar.
*
* @param mToolbar
* the m toolbar
*/
protected void clearToolbar(MToolBar mToolbar) {
// bug in Vaaclipse -> Iterate to remove all
for (Iterator<MToolBarElement> iterator = mToolbar.getChildren()
.iterator(); iterator.hasNext();) {
iterator.next();
iterator.remove();
}
}
/**
* Creates handled tool items for the given exposed action and the
* commandId.
*
* @param yAction
* the y action
* @param commandId
* the command id
* @param initialEnabled
* the initial enabled
* @return the m handled tool item
*/
private MHandledToolItem createToolItem(YExposedAction yAction,
String commandId, boolean initialEnabled) {
MCommand command = findCommand(mPart, commandId);
if (command == null) {
LOGGER.error("No action created for " + yAction.getId()
+ " since command missing: " + commandId);
return null;
}
MHandledToolItem toolItem = MMenuFactory.INSTANCE
.createHandledToolItem();
toolItem.setCommand(command);
String keyBinding = keyBindingService.getKeyBindingString(mPart,
commandId);
String tooltip = i18nService.getValue(yAction.getLabelI18nKey(),
Locale.getDefault());
toolItem.setTooltip(tooltip + " " + keyBinding);
toolItem.setIconURI(i18nService.getValue(yAction.getIcon(),
Locale.getDefault()));
toolItem.setVisible(true);
toolItem.setToBeRendered(true);
commandInfo.put(commandId, new CommandInfo(yAction, toolItem,
initialEnabled, commandId));
return toolItem;
}
/**
* Tries to find the command with the specified id.
*
* @param mPart
* the m part
* @param id
* the id
* @return the m command
*/
private MCommand findCommand(MPart mPart, String id) {
EModelService modelService = eclipseContext.get(EModelService.class);
List<MCommand> commands = modelService.findElements(
eclipseContext.get(MApplication.class), id, MCommand.class,
Collections.<String> emptyList());
MCommand command = null;
if (commands.size() > 0) {
command = commands.get(0);
}
return command;
}
/**
* Dispose.
*/
@PreDestroy
public void dispose() {
try {
if (redirectedEventtopics != null) {
redirectedEventtopics.clear();
redirectedEventtopics = null;
}
exposedActionsCallback = null;
if (eventHandlers != null) {
for (EventHandler handler : eventHandlers) {
e4EventBroker.unsubscribe(handler);
}
eventHandlers.clear();
eventHandlers = null;
}
if (viewContext != null) {
viewContext.dispose();
}
if (ecviewFieldValidationManager != null) {
ecviewFieldValidationManager.dispose();
ecviewFieldValidationManager.removeListener(this);
ecviewFieldValidationManager = null;
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Dispatches events from the event broker to the proper bean slots.
*
* @param event
* the event
*/
protected void dispatchEventBrokerEvent(final Event event) {
if (redirectedEventtopics == null) {
return;
}
final String eventTopic = topicNormalizer.unwrapTopic(event.getTopic());
if (!redirectedEventtopics.containsKey(eventTopic)) {
return;
}
final Object newBean = event.getProperty(IEventBroker.DATA);
// create a runnable processing the set operations
Runnable doRunnable = new Runnable() {
@Override
public void run() {
resetStatus();
for (YBeanSlot yBeanSlot : redirectedEventtopics
.get(eventTopic)) {
final ISlot slot = viewContext.getBeanSlot(yBeanSlot
.getName());
// TODO workaround for databinding -> New instance may be
// polymorphic brother of the current instance. And if
// binding
// the new instance, numeric field will not become unbound.
// So
// lets set a new instance of current set instance before
// setting the new entry.
Object oldValue = slot.getValue();
if (oldValue != null) {
Class<?> valueClass = oldValue.getClass();
try {
// now all fields will become unbound from the
// current
// instance
slot.setValue(valueClass.newInstance());
} catch (Exception e) {
LOGGER.warn("Could not reset the value by {}",
valueClass.getName());
}
}
slot.setValue(newBean);
}
}
};
if (isBeanslotDirty(eventTopic, newBean)) {
// show an accept loosing data dialog
AcceptLoosingDataDialog.showDialog(i18nService, resourceProvider,
doRunnable, null);
} else {
doRunnable.run();
}
}
/**
* Returns true, if one of the bean slots addressed by the eventTopic is
* dirty.
*
* @param eventTopic
* the event topic
* @param newBean
* the new bean
* @return true, if is beanslot dirty
*/
protected boolean isBeanslotDirty(String eventTopic, Object newBean) {
boolean dirty = false;
for (YBeanSlot yBeanSlot : redirectedEventtopics.get(eventTopic)) {
final ISlot slot = viewContext.getBeanSlot(yBeanSlot.getName());
Object currentBean = slot.getValue();
if (currentBean != null && currentBean != newBean) {
try {
dirty = DtoUtils.isDirty(currentBean);
if (dirty) {
// dirty found and leave
break;
}
} catch (IllegalAccessException e) {
// if there is no dirty flag, we just ignore it
}
}
}
return dirty;
}
/**
* Resets the status in the status manager.
*/
protected void resetStatus() {
statusManager.getScopeFor(mPart).clearStatus();
}
/**
* Forwards the enabled state to the e4 tool item.
*/
private class ExposedActionsCallback extends AdapterImpl {
/* (non-Javadoc)
* @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification)
*/
@Override
public void notifyChanged(org.eclipse.emf.common.notify.Notification msg) {
if (msg.getEventType() == org.eclipse.emf.common.notify.Notification.SET) {
if (msg.getFeature() == CoreModelPackage.Literals.YENABLE__ENABLED) {
YExposedAction yAction = (YExposedAction) msg.getNotifier();
CommandInfo info = findCommandInfo(yAction);
if (info != null) {
boolean newEnabled = msg.getNewBooleanValue();
info.toolItem.setEnabled(newEnabled);
info.enabled = newEnabled;
}
} else if (msg.getFeature() == CoreModelPackage.Literals.YEXPOSED_ACTION__INTERNAL_CLICK_TIME) {
YExposedAction yAction = (YExposedAction) msg.getNotifier();
CommandInfo info = findCommandInfo(yAction);
if (info != null) {
MHandledToolItem handledItem = (MHandledToolItem) info.toolItem;
IPartItemExecutionService service = mPart.getContext()
.get(IPartItemExecutionService.class);
if (service != null
&& service.canExecuteItem(handledItem)) {
// notify the exposed action about the external
// click
yAction.setExternalClickTime(new Date().getTime());
service.executeItem(handledItem);
} else {
// notify the action about the cancel
ActionCanceledAdapter.notify(yAction);
}
}
}
}
}
}
/**
* Notifies the action about a cancel.
*/
private static class ActionCanceledAdapter implements Runnable {
/** The action. */
private final YExposedAction action;
/**
* Notify.
*
* @param action
* the action
*/
public static void notify(YExposedAction action) {
action.setCanceledNotificationTime(new Date().getTime());
}
/**
* Instantiates a new action canceled adapter.
*
* @param action
* the action
*/
public ActionCanceledAdapter(YExposedAction action) {
super();
this.action = action;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
notify(action);
}
}
/**
* Notifies the action about their proper execution.
*/
private static class ActionExecutedAdapter implements Runnable {
/** The action. */
private final YExposedAction action;
/**
* Notify.
*
* @param action
* the action
*/
public static void notify(YExposedAction action) {
action.setExecutedNotificationTime(new Date().getTime());
}
/**
* Instantiates a new action executed adapter.
*
* @param action
* the action
*/
public ActionExecutedAdapter(YExposedAction action) {
super();
this.action = action;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
notify(action);
}
}
/**
* Notifies the action about the external click.
*/
private static class ActionExternalClickedAdapter implements Runnable {
/** The action. */
private final YExposedAction action;
/**
* Notify.
*
* @param action
* the action
*/
public static void notify(YExposedAction action) {
action.setExternalClickTime(new Date().getTime());
}
/**
* Instantiates a new action external clicked adapter.
*
* @param action
* the action
*/
@SuppressWarnings("unused")
public ActionExternalClickedAdapter(YExposedAction action) {
super();
this.action = action;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
notify(action);
}
}
/**
* A pojo to keep related objects together. Will be used by the
* GenericECViewPart and also by handlers to determine enabled state.
*/
private static class CommandInfo {
/** The action. */
private YExposedAction action;
/** The tool item. */
private MToolItem toolItem;
/** The enabled. */
// is used to return the enabled state for a request from handler
private boolean enabled;
/** The command id. */
private String commandId;
/**
* Instantiates a new command info.
*
* @param action
* the action
* @param toolItem
* the tool item
* @param enabled
* the enabled
* @param commandId
* the command id
*/
public CommandInfo(YExposedAction action, MToolItem toolItem,
boolean enabled, String commandId) {
super();
this.action = action;
this.toolItem = toolItem;
this.enabled = enabled;
this.commandId = commandId;
}
}
/**
* Returns the command info for the given action.
*
* @param yAction
* the y action
* @return the command info
*/
protected CommandInfo findCommandInfo(final YExposedAction yAction) {
try {
return commandInfo.values().stream()
.filter(e -> e.action == yAction).findFirst().get();
} catch (NoSuchElementException e) {
return null;
}
}
/**
* Returns the command info for the given toolItem.
*
* @param toolItem
* the tool item
* @return the command info
*/
protected CommandInfo findCommandInfo(MToolBarElement toolItem) {
try {
return commandInfo.values().stream()
.filter(e -> e.toolItem == toolItem).findFirst().get();
} catch (NoSuchElementException e) {
return null;
}
}
/**
* Returns the command info for the given commandId.
*
* @param commandId
* the command id
* @return the command info
*/
protected CommandInfo findCommandInfo(final String commandId) {
return commandInfo.get(commandId);
}
}