blob: 20256042a80abe20cabe81293ff50707f5aab1fb [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.problems.views;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.osbp.runtime.common.i18n.II18nService;
import org.eclipse.osbp.runtime.common.validation.IStatus;
import org.eclipse.osbp.runtime.common.validation.IStatus.Severity;
import org.eclipse.osbp.vaaclipse.addons.common.api.IE4Topics;
import org.eclipse.osbp.vaaclipse.addons.common.api.ResourceUtil;
import org.eclipse.osbp.vaaclipse.addons.common.api.status.IStatusManager;
import org.eclipse.osbp.vaaclipse.addons.common.api.status.IStatusScope;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.event.ItemClickEvent;
import com.vaadin.server.Resource;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Label;
import com.vaadin.ui.Table;
import com.vaadin.ui.Table.ColumnHeaderMode;
import com.vaadin.ui.Table.RowHeaderMode;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
/**
* View that shows problems.
*/
public class ProblemsView {
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory
.getLogger(ProblemsView.class);
/** The parent. */
private final VerticalLayout parent;
/** The eclipse context. */
@SuppressWarnings("unused")
private final IEclipseContext eclipseContext;
/** The status manager. */
@Inject
private IStatusManager statusManager;
/** The part service. */
@Inject
private EPartService partService;
/** The event broker. */
@Inject
private IEventBroker eventBroker;
/** The i18n service. */
@Inject
private II18nService i18nService;
/** The table. */
private Table table;
/** The container. */
private BeanItemContainer<StatusBean> container;
/** The current timer. */
private Timer currentTimer;
/** The changed validations handler. */
private EventHandler changedValidationsHandler;
/** The changed scope handler. */
private EventHandler changedScopeHandler;
/** The view mode. */
private Mode viewMode = Mode.FOLLOW_ACTIVE_PART;
/** The freezed scope. */
private IStatusScope freezedScope;
/** The detail label. */
private Label detailLabel;
/**
* Instantiates a new problems view.
*
* @param parent
* the parent
* @param eclipseContext
* the eclipse context
* @param app
* the app
*/
@Inject
public ProblemsView(VerticalLayout parent, IEclipseContext eclipseContext,
MApplication app) {
this.parent = parent;
this.eclipseContext = eclipseContext;
}
/**
* Inits the.
*/
@SuppressWarnings("serial")
@PostConstruct
protected void init() {
table = new Table();
table.setSelectable(true);
table.setSizeFull();
parent.addComponent(table);
container = new BeanItemContainer<StatusBean>(StatusBean.class);
table.setContainerDataSource(container);
table.setVisibleColumns(new Object[] { "message", "fieldName",
"partName", "bundleSymbolicName", "severity", "messagePath",
"creatorClass", "messageCode" });
table.setColumnCollapsingAllowed(true);
table.setColumnCollapsed("bundleSymbolicName", true);
table.setColumnCollapsed("severity", true);
table.setColumnCollapsed("creatorClass", true);
table.setColumnCollapsed("messageCode", true);
table.setRowHeaderMode(RowHeaderMode.ICON_ONLY);
table.setItemIconPropertyId("severityImage");
table.setColumnHeaderMode(ColumnHeaderMode.EXPLICIT_DEFAULTS_ID);
Locale locale = UI.getCurrent().getLocale();
table.setColumnHeader("message",
toViewI18n("message", i18nService, locale));
table.setColumnHeader("fieldName",
toViewI18n("fieldName", i18nService, locale));
table.setColumnHeader("partName",
toViewI18n("partName", i18nService, locale));
table.setColumnHeader("bundleSymbolicName",
toViewI18n("bundleSymbolicName", i18nService, locale));
table.setColumnHeader("severity",
toViewI18n("severity", i18nService, locale));
table.setColumnHeader("messagePath",
toViewI18n("messagePath", i18nService, locale));
table.setColumnHeader("creatorClass",
toViewI18n("creatorClass", i18nService, locale));
table.setColumnHeader("messageCode",
toViewI18n("messageCode", i18nService, locale));
table.addItemClickListener(new ItemClickEvent.ItemClickListener() {
@Override
public void itemClick(ItemClickEvent event) {
if (event.isDoubleClick()) {
focusStatus(event);
}
}
});
table.addValueChangeListener(new Property.ValueChangeListener() {
@Override
public void valueChange(ValueChangeEvent event) {
showDetailMessage((StatusBean) event.getProperty().getValue());
}
});
detailLabel = new Label();
detailLabel.setPrimaryStyleName("detailmessage");
detailLabel.setContentMode(ContentMode.HTML);
detailLabel.setSizeFull();
parent.addComponent(detailLabel);
parent.setExpandRatio(table, 0.85f);
parent.setExpandRatio(detailLabel, 0.15f);
// initialize the view mode
setMode(viewMode);
// handle changed validation scopes
//
changedScopeHandler = new EventHandler() {
@Override
public void handleEvent(Event event) {
switch (viewMode) {
case FREEZE_SCOPE:
// only refresh contents if the changed scope is the freezed
// scope
IStatusScope scope = getScopeFromEvent(event);
if (freezedScope == scope) {
refreshContent();
}
break;
case FOLLOW_ACTIVE_PART:
case SHOW_ALL:
default:
refreshContent();
break;
}
}
};
// handle changes in validation results
//
eventBroker.subscribe(
IE4Topics.StatusManagerEvents.ACTIVE_SCOPE_CHANGED_TOPIC,
changedScopeHandler);
changedValidationsHandler = new EventHandler() {
@Override
public void handleEvent(Event event) {
switch (viewMode) {
case FREEZE_SCOPE:
// only refresh contents if the changed scope is the freezed
// scope
IStatusScope scope = getScopeFromEvent(event);
if (freezedScope == scope) {
refreshContent();
}
break;
case FOLLOW_ACTIVE_PART:
case SHOW_ALL:
default:
refreshContent();
break;
}
}
};
eventBroker.subscribe(
IE4Topics.StatusManagerEvents.VALIDATIONS_CHANGED_TOPIC,
changedValidationsHandler);
}
/**
* To view i18n.
*
* @param key
* the key
* @param i18nService
* the i18n service
* @param locale
* the locale
* @return the string
*/
private static String toViewI18n(String key, II18nService i18nService,
Locale locale) {
String result = i18nService.getValue(
"org.eclipse.osbp.vaaclipse.addons.application.views.ProblemsView."
+ key, locale);
if (result == null || result.equals("")) {
return key;
}
return result;
}
/**
* To common i18n.
*
* @param key
* the key
* @param i18nService
* the i18n service
* @param locale
* the locale
* @return the string
*/
private static String toCommonI18n(String key, II18nService i18nService,
Locale locale) {
if (key == null) {
return "";
}
String result = i18nService.getValue(key, locale);
if (result == null || result.equals("")) {
return key;
}
return result;
}
/**
* Shows the detail message.
*
* @param statusBean
* the status bean
*/
protected void showDetailMessage(StatusBean statusBean) {
if (statusBean != null) {
detailLabel.setValue(statusBean.getMessage().getValue());
} else {
detailLabel.setValue(null);
}
}
/**
* Tries to put the focus to the field involved in the problem.
*
* @param event
* the event
*/
protected void focusStatus(ItemClickEvent event) {
StatusBean status = (StatusBean) event.getItemId();
String mPartId = status.getPartId();
if (mPartId != null && !mPartId.equals("")) {
MPart mPart = partService.findPart(mPartId);
if (mPart != null) {
partService.bringToTop(mPart);
partService.activate(mPart);
String fieldId = status.getFieldId();
if (fieldId != null && !fieldId.equals("")) {
// send a focus field event to the mpart
eventBroker.post(IE4Topics.PartEvents.FOCUS_FIELD_TOPIC,
createFocusFieldEvent(mPartId, fieldId));
}
}
}
}
/**
* Creates an event to focus a field in a MPart.
*
* @param mPartId
* the m part id
* @param fieldId
* the field id
* @return the map
*/
protected Map<String, Object> createFocusFieldEvent(String mPartId,
String fieldId) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(IE4Topics.PartEvents.PROP_MPART_ID, mPartId);
properties.put(IE4Topics.PartEvents.PROP_FIELD_ID, fieldId);
return properties;
}
/**
* Refreshes the content of the active MPart.
*/
protected void refreshContent() {
if (table == null || table.getUI() == null) {
return;
}
synchronized (this) {
// Wait for 250ms before refreshing the table. Most probably several
// validation events will
// arrive in the next milliseconds
if (currentTimer == null) {
currentTimer = new Timer();
currentTimer.schedule(new TimerTask() {
@Override
public void run() {
table.getUI().accessSynchronously(new Runnable() {
@Override
public void run() {
container.removeAllItems();
if (viewMode == Mode.FOLLOW_ACTIVE_PART
|| viewMode == Mode.FREEZE_SCOPE) {
IStatusScope scope = statusManager
.getActiveScope();
if (scope != null) {
container.addAll(mapStatus(scope));
}
} else if (viewMode == Mode.SHOW_ALL) {
container.addAll(mapStatus(statusManager));
}
detailLabel.setValue("");
LOGGER.debug("Table refreshed.");
resetTimer();
}
});
}
}, 250);
LOGGER.debug("Scheduled Table-Refresh-Timer.");
} else {
LOGGER.debug("Timer already active.");
}
}
}
/**
* Reset timer.
*/
protected void resetTimer() {
synchronized (this) {
currentTimer = null;
}
}
/**
* Map status.
*
* @param scope
* the scope
* @return the list
*/
protected List<StatusBean> mapStatus(IStatusScope scope) {
List<StatusBean> result = new ArrayList<ProblemsView.StatusBean>();
for (IStatus status : scope.getAllStatus()) {
result.add(StatusBean.create(status, i18nService,
parent.getLocale()));
}
return result;
}
/**
* Map status.
*
* @param manager
* the manager
* @return the list
*/
protected List<StatusBean> mapStatus(IStatusManager manager) {
List<StatusBean> result = new ArrayList<ProblemsView.StatusBean>();
for (IStatus status : manager.getAllScopeStatus()) {
result.add(StatusBean.create(status, i18nService,
parent.getLocale()));
}
return result;
}
/**
* Sets the mode.
*
* @param mode
* the new mode
*/
public void setMode(Mode mode) {
switch (mode) {
case FREEZE_SCOPE:
this.freezedScope = statusManager.getActiveScope();
break;
case FOLLOW_ACTIVE_PART:
case SHOW_ALL:
this.freezedScope = null;
}
this.viewMode = mode;
refreshContent();
}
/**
* Dispose.
*/
@PreDestroy
protected void dispose() {
eventBroker.unsubscribe(changedScopeHandler);
eventBroker.unsubscribe(changedValidationsHandler);
changedScopeHandler = null;
changedValidationsHandler = null;
table = null;
container = null;
}
/**
* Gets the scope from event.
*
* @param event
* the event
* @return the scope from event
*/
protected IStatusScope getScopeFromEvent(Event event) {
@SuppressWarnings("unchecked")
Map<String, Object> props = (Map<String, Object>) event
.getProperty(IEventBroker.DATA);
IStatusScope scope = (IStatusScope) props
.get(IE4Topics.StatusManagerEvents.ACTIVE_SCOPE_CHANGED_TOPIC);
return scope;
}
/**
* The mode of the view.
*/
public enum Mode {
/** Follows the active MPart. */
FOLLOW_ACTIVE_PART,
/** Only shows messages for the current scope. */
FREEZE_SCOPE,
/** Show all messages. */
SHOW_ALL
}
/**
* The Class StatusBean.
*/
public static class StatusBean {
/** The severity image. */
@SuppressWarnings("unused")
private Resource severityImage;
/** The severity. */
private Severity severity;
/** The bundle symbolic name. */
private String bundleSymbolicName;
/** The message code. */
private String messageCode;
/** The message. */
private Label message;
/** The exception. */
private Exception exception;
/** The part id. */
private String partId;
/** The field id. */
private String fieldId;
/** The part name. */
private String partName;
/** The field name. */
private String fieldName;
/** The message path. */
private String messagePath;
/** The creator class. */
private String creatorClass;
/**
* Creates the.
*
* @param status
* the status
* @param i18nService
* the i18n service
* @param locale
* the locale
* @return the status bean
*/
public static StatusBean create(IStatus status,
II18nService i18nService, Locale locale) {
StatusBean bean = new StatusBean();
bean.severity = status.getSeverity();
bean.bundleSymbolicName = status.getBundleSymblicName();
bean.messageCode = status.getCode();
bean.message = new Label(status.getMessage(), ContentMode.HTML);
bean.exception = status.getException();
bean.partId = (String) status
.getProperty(IStatus.PROP_UI_APPLICATION_ID);
bean.fieldId = (String) status.getProperty(IStatus.PROP_FIELD_ID);
String fieldI18nKey = (String) status
.getProperty(IStatus.PROP_FIELD_I18N_KEY);
bean.partName = toCommonI18n(bean.partId, i18nService, UI
.getCurrent().getLocale());
if (fieldI18nKey != null && !fieldI18nKey.equals("")) {
bean.fieldName = toCommonI18n(fieldI18nKey, i18nService, UI
.getCurrent().getLocale());
} else {
bean.fieldName = toCommonI18n(bean.fieldId, i18nService, UI
.getCurrent().getLocale());
}
bean.creatorClass = (String) status
.getProperty(IStatus.PROP_CREATOR);
bean.messagePath = (String) status
.getProperty(IStatus.PROP_JAVAX_PROPERTY_PATH);
return bean;
}
/**
* Gets the severity image.
*
* @return the severityImage
*/
@SuppressWarnings("incomplete-switch")
public Resource getSeverityImage() {
switch (severity) {
case OK:
case INFO:
return ResourceUtil
.getResource("platform:/plugin/org.eclipse.osbp.vaaclipse.addons.application/images/info_tsk.png");
case WARNING:
return ResourceUtil
.getResource("platform:/plugin/org.eclipse.osbp.vaaclipse.addons.application/images/warn_tsk.png");
case ERROR:
case CRITICAL:
case SYSTEMERROR:
return ResourceUtil
.getResource("platform:/plugin/org.eclipse.osbp.vaaclipse.addons.application/images/error_tsk.png");
}
return null;
}
/**
* Sets the severity image.
*
* @param severityImage
* the severityImage to set
*/
public void setSeverityImage(Resource severityImage) {
this.severityImage = severityImage;
}
/**
* Gets the severity.
*
* @return the severity
*/
public Severity getSeverity() {
return severity;
}
/**
* Sets the severity.
*
* @param severity
* the severity to set
*/
public void setSeverity(Severity severity) {
this.severity = severity;
}
/**
* Gets the bundle symbolic name.
*
* @return the bundleSymbolicName
*/
public String getBundleSymbolicName() {
return bundleSymbolicName;
}
/**
* Sets the bundle symbolic name.
*
* @param bundleSymbolicName
* the bundleSymbolicName to set
*/
public void setBundleSymbolicName(String bundleSymbolicName) {
this.bundleSymbolicName = bundleSymbolicName;
}
/**
* Gets the message.
*
* @return the message
*/
public Label getMessage() {
return message;
}
/**
* Sets the message.
*
* @param message
* the message to set
*/
public void setMessage(Label message) {
this.message = message;
}
/**
* Gets the exception.
*
* @return the exception
*/
public Exception getException() {
return exception;
}
/**
* Sets the exception.
*
* @param exception
* the exception to set
*/
public void setException(Exception exception) {
this.exception = exception;
}
/**
* Gets the part id.
*
* @return the partId
*/
public String getPartId() {
return partId;
}
/**
* Sets the part id.
*
* @param partId
* the partId to set
*/
public void setPartId(String partId) {
this.partId = partId;
}
/**
* Gets the field id.
*
* @return the fieldId
*/
public String getFieldId() {
return fieldId;
}
/**
* Sets the field id.
*
* @param fieldId
* the fieldId to set
*/
public void setFieldId(String fieldId) {
this.fieldId = fieldId;
}
/**
* Gets the part name.
*
* @return the partName
*/
public String getPartName() {
return partName;
}
/**
* Sets the part name.
*
* @param partName
* the partName to set
*/
public void setPartName(String partName) {
this.partName = partName;
}
/**
* Gets the field name.
*
* @return the fieldName
*/
public String getFieldName() {
return fieldName;
}
/**
* Sets the field name.
*
* @param fieldName
* the fieldName to set
*/
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
/**
* Gets the message path.
*
* @return the messagePath
*/
public String getMessagePath() {
return messagePath;
}
/**
* Sets the message path.
*
* @param messagePath
* the messagePath to set
*/
public void setMessagePath(String messagePath) {
this.messagePath = messagePath;
}
/**
* Gets the message code.
*
* @return the messageCode
*/
public String getMessageCode() {
return messageCode;
}
/**
* Sets the message code.
*
* @param messageCode
* the messageCode to set
*/
public void setMessageCode(String messageCode) {
this.messageCode = messageCode;
}
/**
* Gets the creator class.
*
* @return the creatorClass
*/
public String getCreatorClass() {
return creatorClass;
}
/**
* Sets the creator class.
*
* @param creatorClass
* the creatorClass to set
*/
public void setCreatorClass(String creatorClass) {
this.creatorClass = creatorClass;
}
}
}