X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.event%2Fsrc%2Forg%2Fsimantics%2Fevent%2Fview%2FEventView.java;h=4a784b1c7fb1d19e7aa50dbfc351610ecd80ecff;hb=refs%2Fchanges%2F38%2F238%2F2;hp=409378ca4da8541e8d21a0bb7d3603e6618ec7d9;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.event/src/org/simantics/event/view/EventView.java b/bundles/org.simantics.event/src/org/simantics/event/view/EventView.java index 409378ca4..4a784b1c7 100644 --- a/bundles/org.simantics.event/src/org/simantics/event/view/EventView.java +++ b/bundles/org.simantics.event/src/org/simantics/event/view/EventView.java @@ -1,569 +1,569 @@ -/******************************************************************************* - * Copyright (c) 2007, 2011 Association for Decentralized Information Management - * in Industry THTH ry. - * 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: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.event.view; - -import gnu.trove.map.hash.THashMap; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.eclipse.jface.layout.GridDataFactory; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Text; -import org.eclipse.swt.widgets.Tree; -import org.eclipse.swt.widgets.TreeColumn; -import org.eclipse.ui.IMemento; -import org.eclipse.ui.IWorkbenchSite; -import org.simantics.Simantics; -import org.simantics.browsing.ui.BuiltinKeys; -import org.simantics.browsing.ui.Column; -import org.simantics.browsing.ui.Column.Align; -import org.simantics.browsing.ui.GraphExplorer; -import org.simantics.browsing.ui.NodeContext; -import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey; -import org.simantics.browsing.ui.NodeQueryManager; -import org.simantics.browsing.ui.PrimitiveQueryUpdater; -import org.simantics.browsing.ui.common.EvaluatorData; -import org.simantics.browsing.ui.common.EvaluatorData.Evaluator; -import org.simantics.browsing.ui.common.comparators.ImmutableLexicalComparable; -import org.simantics.browsing.ui.common.processors.AbstractPrimitiveQueryProcessor; -import org.simantics.browsing.ui.common.processors.ProcessorLifecycle; -import org.simantics.browsing.ui.content.ComparableContext; -import org.simantics.browsing.ui.content.ComparableContextFactory; -import org.simantics.browsing.ui.content.Labeler; -import org.simantics.browsing.ui.swt.widgets.GraphExplorerComposite; -import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; -import org.simantics.databoard.Bindings; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.common.procedure.adapter.DisposableListener; -import org.simantics.db.common.request.ObjectsWithType; -import org.simantics.db.common.request.UniqueRead; -import org.simantics.db.common.uri.UnescapedChildMapOfResource; -import org.simantics.db.common.utils.Logger; -import org.simantics.db.exception.BindingException; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.exception.DoesNotContainValueException; -import org.simantics.db.exception.NoSingleResultException; -import org.simantics.db.exception.ServiceException; -import org.simantics.db.management.ISessionContext; -import org.simantics.db.service.QueryControl; -import org.simantics.event.ontology.EventResource; -import org.simantics.event.util.EventUtils; -import org.simantics.operation.Layer0X; -import org.simantics.simulation.ontology.SimulationResource; -import org.simantics.ui.workbench.IPropertyPage; -import org.simantics.utils.datastructures.ArrayMap; -import org.simantics.utils.strings.AlphanumComparator; -import org.simantics.utils.ui.workbench.StringMemento; -import org.simantics.views.swt.SimanticsView; - -/** - * @author Tuukka Lehtonen - */ -public class EventView extends SimanticsView { - - private static final String EVENTS_TABLE_MEMENTO_ID = "events"; - private static final String BROWSE_CONTEXT = "http://www.simantics.org/Event-1.2/View/EventBrowseContext"; - private static final String CONTEXT_MENU_ID = "org.simantics.event.view.popup"; - - private GraphExplorerComposite eventExplorer; - private TableComparatorFactory comparator; - - - @Override - protected Set getBrowseContexts() { - return Collections.singleton(BROWSE_CONTEXT); - } - - @Override - protected String getContextMenuId() { - return CONTEXT_MENU_ID; - } - - @Override - protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support) { - - Column[] COLUMNS = new Column[] { - //new Column(ColumnKeys.SINGLE, "S", Align.LEFT, 40, "S", false), - new Column(Constants.COLUMN_EVENT_INDEX, "#", Align.LEFT, 70, "Event Index", false), - new Column(Constants.COLUMN_TIMESTAMP, "Time Stamp", Align.LEFT, 100, "Time Stamp", false), - new Column(Constants.COLUMN_MILESTONE, "M", Align.LEFT, 24, "Is Milestone?", false), - new Column(Constants.COLUMN_EVENT_TYPE, "Type", Align.LEFT, 110, "Event Type", false), - new Column(Constants.COLUMN_RETURNED, "R", Align.LEFT, 24, "Has Event Returned?", false), - new Column(Constants.COLUMN_TAG_NAME, "Tag", Align.LEFT, 120, "Tag Name", true, 1), - new Column(Constants.COLUMN_MESSAGE, "Message", Align.LEFT, 100, "Message", true, 2), - new Column(Constants.COLUMN_RETURN_TIME, "Return Time", Align.LEFT, 100, "Return Event Time Stamp", false), - }; - - final Text headerText = new Text(body, SWT.NONE); - - final DisposableListener headerTextListener = new DisposableListener() { - - @Override - public void execute(final String result) { - Display d = headerText.getDisplay(); - if(d.isDisposed()) return; - d.asyncExec(new Runnable() { - - @Override - public void run() { - if(headerText.isDisposed()) return; - headerText.setText(result); - headerText.getParent().layout(); - } - - }); - } - - @Override - public void exception(Throwable t) { - Logger.defaultLogError(t); - } - - }; - - headerText.addDisposeListener(new DisposeListener() { - - @Override - public void widgetDisposed(DisposeEvent e) { - headerTextListener.dispose(); - } - - }); - - headerText.setText(""); - headerText.setBackground(body.getBackground()); - GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).grab(true, false).span(2, 1).applyTo(headerText); - - Simantics.getSession().asyncRequest(new UniqueRead() { - - public String getChildrenImpl(ReadGraph graph, Resource project, List eventLogs) throws DatabaseException { - - int discardedEvents = 0; - - for(Resource log : eventLogs) { - - Map slices = graph.syncRequest(new UnescapedChildMapOfResource(log)); - if(slices.isEmpty()) continue; - ArrayList keys = new ArrayList(slices.keySet()); - Collections.sort(keys, AlphanumComparator.COMPARATOR); - Integer base = Integer.parseInt(keys.get(0)); - discardedEvents += EventUtils.SLICE_SIZE * base; - - } - - if(discardedEvents > 0) - return discardedEvents + " events in chronological order have been automatically discarded from event log"; - else - return ""; - - } - - @Override - public String perform(ReadGraph graph) throws DatabaseException { - - List eventLogs = EventView.getEventLogs(graph); - QueryControl qc = graph.getService(QueryControl.class); - return getChildrenImpl(qc.getIndependentGraph(graph), Simantics.getProjectResource(), eventLogs); - - } - - }, headerTextListener); - - eventExplorer = new GraphExplorerComposite( - ArrayMap.keys("displaySelectors", "displayFilter", "maxChildren").values(false, false, 5000), - site, body, support, SWT.FULL_SELECTION | SWT.MULTI) { - @Override - protected void initializeExplorerWithEvaluator(GraphExplorer explorer, ISessionContext context, - EvaluatorData data) { - Evaluator eval = data.newEvaluator(); - IMemento m = memento; - if (comparator != null) { - m = new StringMemento(); - comparator.saveState(EVENTS_TABLE_MEMENTO_ID, m); - } - comparator = new TableComparatorFactory(explorer); - if (m != null) - comparator.restoreState(EVENTS_TABLE_MEMENTO_ID, m); - eval.addComparator(comparator, 2.0); - data.addEvaluator(Object.class, eval); - } - }; - eventExplorer.setBrowseContexts(getBrowseContexts()); - eventExplorer.setContextMenuId(getContextMenuId()); - eventExplorer.setColumns(COLUMNS); - eventExplorer.finish(); - GridDataFactory.fillDefaults().grab(true, true).span(2, 1).applyTo(eventExplorer); - - attachHeaderListeners(eventExplorer); - } - - public static List getEventLogs(ReadGraph graph) throws NoSingleResultException, DoesNotContainValueException, BindingException, ServiceException, DatabaseException { - Layer0X L0X = Layer0X.getInstance(graph); - SimulationResource SIMU = SimulationResource.getInstance(graph); - EventResource EVENT = EventResource.getInstance(graph); - - Resource project = Simantics.getProjectResource(); - - List eventLogs = new ArrayList(); - for (Resource activeModel : graph.syncRequest(new ObjectsWithType(project, L0X.Activates, SIMU.Model))) { - for (Resource eventLog : graph.syncRequest(new ObjectsWithType(activeModel, EVENT.HasEventLog, EVENT.EventLog))) { - if (!graph.hasStatement(eventLog, EVENT.Hidden)) { - eventLogs.add(eventLog); -// graph.getRelatedValue(eventLog, EVENT.HasModificationCounter, Bindings.INTEGER); - } - } - } - return eventLogs; - } - - @Override - public void saveState(IMemento memento) { - super.saveState(memento); - if (comparator != null) - comparator.saveState(EVENTS_TABLE_MEMENTO_ID, memento); - } - - private void attachHeaderListeners(GraphExplorerComposite explorer) { - final Tree tree = explorer.getExplorerControl(); - - String sortColumn = null; - if (comparator != null) { - sortColumn = comparator.getColumn(); - } - - for (final TreeColumn column : tree.getColumns()) { - column.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - Column c = (Column) column.getData(); - String key = transformColumn( c.getKey() ); - - comparator.setColumn(key); - comparator.toggleAscending(key); - comparator.refresh(); - - // Visualizes the selected sorting in the Tree column header - tree.setSortColumn(column); - tree.setSortDirection(comparator.isAscending(key) ? SWT.DOWN : SWT.UP); - } - }); - - Column c = (Column) column.getData(); - String key = transformColumn( c.getKey() ); - if (key.equals(sortColumn)) { - tree.setSortColumn(column); - tree.setSortDirection(comparator.isAscending(key) ? SWT.DOWN : SWT.UP); - } - } - } - - private Class getColumnType(String key) { - if (Constants.COLUMN_EVENT_INDEX.equals(key)) - return Long.class; - if (Constants.COLUMN_TIMESTAMP_NUMERIC.equals(key) - || Constants.COLUMN_RETURN_TIME_NUMERIC.equals(key)) - return Double.class; - return String.class; - } - - private String transformColumn(String key) { - // HACK: for proper numeric timestamp sorting since - // formatted time values don't sort all that well. - if (Constants.COLUMN_TIMESTAMP.equals(key)) - key = Constants.COLUMN_TIMESTAMP_NUMERIC; - if (Constants.COLUMN_RETURN_TIME.equals(key)) - key = Constants.COLUMN_RETURN_TIME_NUMERIC; - return key; - } - - @Override - public void setFocus() { - eventExplorer.setFocus(); - } - - @Override - protected IPropertyPage getPropertyPage() { - return null; - } - - public class TableComparatorFactory implements ComparableContextFactory { - - private static final String SORTING = "sorting"; - private final static String SORT_COL = "sortCol"; - private final static String COLS = "cols"; - private final static String COL_ASCEND = "ascend"; - - UpdateTrigger updateTrigger = new UpdateTrigger(); - - /** Current sort column */ - private String column = null; - /** Ascending state of each column */ - private Map ascendMap = new HashMap(); - - public TableComparatorFactory(GraphExplorer ge) { - ge.setPrimitiveProcessor(updateTrigger); - } - - public void saveState(String id, IMemento memento) { - if (id == null) - throw new NullPointerException("null id"); - IMemento m = null; - for (IMemento child : memento.getChildren(SORTING)) { - if (id.equals(child.getID())) - m = child; - } - if (m == null) - m = memento.createChild(SORTING, id); - if (getColumn() != null) - m.putString(SORT_COL, getColumn()); - - columns: - for (String columnKey : ascendMap.keySet()) { - for (IMemento col : m.getChildren(COLS)) { - if (columnKey.equals(col.getID())) { - col.putBoolean(COL_ASCEND, isAscending(columnKey)); - continue columns; - } - } - IMemento col = m.createChild(COLS, columnKey); - col.putBoolean(COL_ASCEND, isAscending(columnKey)); - } - } - - public void restoreState(String id, IMemento memento) { - if (id == null) - throw new NullPointerException("null id"); - if (!hasState(id, memento)) - return; - for (IMemento m : memento.getChildren(SORTING)) { - if (!id.equals(m.getID())) - continue; - for (IMemento column : m.getChildren(COLS)) { - setAscending( column.getID(), column.getBoolean(COL_ASCEND) ); - } - setColumn( m.getString(SORT_COL) ); - break; - } - } - - public boolean hasState(String id, IMemento memento) { - for (IMemento m : memento.getChildren(SORTING)) - if (id.equals(m.getID())) - return true; - return false; - } - - public void setAscending(String column, Boolean ascending) { - if (ascending == null) - ascendMap.remove(column); - else - ascendMap.put(column, ascending); - } - - public boolean isAscending(String column) { - return !Boolean.FALSE.equals(ascendMap.get(column)); - } - - public void setColumn(String column) { - this.column = column; - } - - public void toggleAscending(String column) { - Boolean ascending = ascendMap.get(column); - if (ascending == null) { - ascendMap.put(column, Boolean.TRUE); - } else { - ascendMap.put(column, !ascending); - } - } - - public void refresh() { - updateTrigger.refresh(); - } - - public String getColumn() { - return column; - } - - @Override - public ComparableContext[] create(NodeQueryManager manager, NodeContext parent, NodeContext[] children) { - // To make this query refreshable. - manager.query(parent, UpdateTrigger.KEY); - - // Don't sort if no sort column is defined - if (column == null) - return null; - - final boolean _ascending = isAscending(column); - final Class _clazz = getColumnType(column); - - ComparableContext[] result = new ComparableContext[children.length]; - for (int i = 0; i < children.length; i++) { - NodeContext child = children[i]; - Labeler labeler = manager.query(child, BuiltinKeys.SELECTED_LABELER); - - int category = (labeler != null) ? labeler.getCategory() : 0; - String label = (labeler != null && column != null) ? labeler.getLabels().get(column) : ""; - if (label == null) - label = ""; - - result[i] = new ImmutableLexicalComparable(category, label, child) { - final boolean ascending = _ascending; - final Class clazz = _clazz; - - @SuppressWarnings("unchecked") - @Override - public int compareTo(ComparableContext arg0) { - ImmutableLexicalComparable other = (ImmutableLexicalComparable) arg0; - - int catDelta = getCategory() - other.getCategory(); - if (!ascending) - catDelta = -catDelta; - if (catDelta != 0) - return catDelta; - - String label1 = getLabel(); - String label2 = other.getLabel(); - - @SuppressWarnings("rawtypes") - Comparator comparator = AlphanumComparator.CASE_INSENSITIVE_COMPARATOR; - if (clazz == Double.class) { - comparator = DoubleStringComparator.INSTANCE; - } else if (clazz == Long.class) { - comparator = LongStringComparator.INSTANCE; - } - - return ascending ? comparator.compare(label1, label2) - : comparator.compare(label2, label1); - } - }; - } - - return result; - } - - @Override - public String toString() { - return "Event Table Sort"; - } - - } - - private static class LongStringComparator implements Comparator { - - private static final LongStringComparator INSTANCE = new LongStringComparator(); - - @Override - public int compare(String s1, String s2) { - long n1 = parseLong(s1); - long n2 = parseLong(s2); - return compare(n1, n2); - } - - private long parseLong(String s) { - try { - return Long.parseLong(s); - } catch (NumberFormatException e1) { - return Long.MIN_VALUE; - } - } - - public static int compare(long x, long y) { - return (x < y) ? -1 : ((x == y) ? 0 : 1); - } - } - - private static class DoubleStringComparator implements Comparator { - - private static final DoubleStringComparator INSTANCE = new DoubleStringComparator(); - - @Override - public int compare(String s1, String s2) { - double n1 = parseDouble(s1); - double n2 = parseDouble(s2); - return Double.compare(n1, n2); - } - - private double parseDouble(String s) { - try { - return Double.parseDouble(s); - } catch (NumberFormatException e2) { - return Double.NaN; - } - } - } - - public static class UpdateTrigger extends AbstractPrimitiveQueryProcessor implements ProcessorLifecycle { - - private Double random = Math.random(); - private Map contexts = new THashMap(); - - public static final PrimitiveQueryKey KEY = new PrimitiveQueryKey() { - @Override - public String toString() { - return "TRIGGER"; - } - }; - - @Override - public Object getIdentifier() { - return KEY; - } - - public void refresh() { - random = Math.random(); - for (Map.Entry entry : contexts.entrySet()) { - entry.getValue().scheduleReplace(entry.getKey(), KEY, random); - } - } - - @Override - public Double query(PrimitiveQueryUpdater updater, NodeContext context, PrimitiveQueryKey key) { - this.contexts.put(context, updater); - return random; - } - - @Override - public String toString() { - return "UpdateTrigger"; - } - - @Override - public void attached(GraphExplorer explorer) { - } - - @Override - public void detached(GraphExplorer explorer) { - clear(); - } - - @Override - public void clear() { - contexts.clear(); - } - - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2011 Association for Decentralized Information Management + * in Industry THTH ry. + * 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.event.view; + +import gnu.trove.map.hash.THashMap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.IWorkbenchSite; +import org.simantics.Simantics; +import org.simantics.browsing.ui.BuiltinKeys; +import org.simantics.browsing.ui.Column; +import org.simantics.browsing.ui.Column.Align; +import org.simantics.browsing.ui.GraphExplorer; +import org.simantics.browsing.ui.NodeContext; +import org.simantics.browsing.ui.NodeContext.PrimitiveQueryKey; +import org.simantics.browsing.ui.NodeQueryManager; +import org.simantics.browsing.ui.PrimitiveQueryUpdater; +import org.simantics.browsing.ui.common.EvaluatorData; +import org.simantics.browsing.ui.common.EvaluatorData.Evaluator; +import org.simantics.browsing.ui.common.comparators.ImmutableLexicalComparable; +import org.simantics.browsing.ui.common.processors.AbstractPrimitiveQueryProcessor; +import org.simantics.browsing.ui.common.processors.ProcessorLifecycle; +import org.simantics.browsing.ui.content.ComparableContext; +import org.simantics.browsing.ui.content.ComparableContextFactory; +import org.simantics.browsing.ui.content.Labeler; +import org.simantics.browsing.ui.swt.widgets.GraphExplorerComposite; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.procedure.adapter.DisposableListener; +import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.common.uri.UnescapedChildMapOfResource; +import org.simantics.db.common.utils.Logger; +import org.simantics.db.exception.BindingException; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.DoesNotContainValueException; +import org.simantics.db.exception.NoSingleResultException; +import org.simantics.db.exception.ServiceException; +import org.simantics.db.management.ISessionContext; +import org.simantics.db.service.QueryControl; +import org.simantics.event.ontology.EventResource; +import org.simantics.event.util.EventUtils; +import org.simantics.operation.Layer0X; +import org.simantics.simulation.ontology.SimulationResource; +import org.simantics.ui.workbench.IPropertyPage; +import org.simantics.utils.datastructures.ArrayMap; +import org.simantics.utils.strings.AlphanumComparator; +import org.simantics.utils.ui.workbench.StringMemento; +import org.simantics.views.swt.SimanticsView; + +/** + * @author Tuukka Lehtonen + */ +public class EventView extends SimanticsView { + + private static final String EVENTS_TABLE_MEMENTO_ID = "events"; + private static final String BROWSE_CONTEXT = "http://www.simantics.org/Event-1.2/View/EventBrowseContext"; + private static final String CONTEXT_MENU_ID = "org.simantics.event.view.popup"; + + private GraphExplorerComposite eventExplorer; + private TableComparatorFactory comparator; + + + @Override + protected Set getBrowseContexts() { + return Collections.singleton(BROWSE_CONTEXT); + } + + @Override + protected String getContextMenuId() { + return CONTEXT_MENU_ID; + } + + @Override + protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support) { + + Column[] COLUMNS = new Column[] { + //new Column(ColumnKeys.SINGLE, "S", Align.LEFT, 40, "S", false), + new Column(Constants.COLUMN_EVENT_INDEX, "#", Align.LEFT, 70, "Event Index", false), + new Column(Constants.COLUMN_TIMESTAMP, "Time Stamp", Align.LEFT, 100, "Time Stamp", false), + new Column(Constants.COLUMN_MILESTONE, "M", Align.LEFT, 24, "Is Milestone?", false), + new Column(Constants.COLUMN_EVENT_TYPE, "Type", Align.LEFT, 110, "Event Type", false), + new Column(Constants.COLUMN_RETURNED, "R", Align.LEFT, 24, "Has Event Returned?", false), + new Column(Constants.COLUMN_TAG_NAME, "Tag", Align.LEFT, 120, "Tag Name", true, 1), + new Column(Constants.COLUMN_MESSAGE, "Message", Align.LEFT, 100, "Message", true, 2), + new Column(Constants.COLUMN_RETURN_TIME, "Return Time", Align.LEFT, 100, "Return Event Time Stamp", false), + }; + + final Text headerText = new Text(body, SWT.NONE); + + final DisposableListener headerTextListener = new DisposableListener() { + + @Override + public void execute(final String result) { + Display d = headerText.getDisplay(); + if(d.isDisposed()) return; + d.asyncExec(new Runnable() { + + @Override + public void run() { + if(headerText.isDisposed()) return; + headerText.setText(result); + headerText.getParent().layout(); + } + + }); + } + + @Override + public void exception(Throwable t) { + Logger.defaultLogError(t); + } + + }; + + headerText.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + headerTextListener.dispose(); + } + + }); + + headerText.setText(""); + headerText.setBackground(body.getBackground()); + GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).grab(true, false).span(2, 1).applyTo(headerText); + + Simantics.getSession().asyncRequest(new UniqueRead() { + + public String getChildrenImpl(ReadGraph graph, Resource project, List eventLogs) throws DatabaseException { + + int discardedEvents = 0; + + for(Resource log : eventLogs) { + + Map slices = graph.syncRequest(new UnescapedChildMapOfResource(log)); + if(slices.isEmpty()) continue; + ArrayList keys = new ArrayList(slices.keySet()); + Collections.sort(keys, AlphanumComparator.COMPARATOR); + Integer base = Integer.parseInt(keys.get(0)); + discardedEvents += EventUtils.SLICE_SIZE * base; + + } + + if(discardedEvents > 0) + return discardedEvents + " events in chronological order have been automatically discarded from event log"; + else + return ""; + + } + + @Override + public String perform(ReadGraph graph) throws DatabaseException { + + List eventLogs = EventView.getEventLogs(graph); + QueryControl qc = graph.getService(QueryControl.class); + return getChildrenImpl(qc.getIndependentGraph(graph), Simantics.getProjectResource(), eventLogs); + + } + + }, headerTextListener); + + eventExplorer = new GraphExplorerComposite( + ArrayMap.keys("displaySelectors", "displayFilter", "maxChildren").values(false, false, 5000), + site, body, support, SWT.FULL_SELECTION | SWT.MULTI) { + @Override + protected void initializeExplorerWithEvaluator(GraphExplorer explorer, ISessionContext context, + EvaluatorData data) { + Evaluator eval = data.newEvaluator(); + IMemento m = memento; + if (comparator != null) { + m = new StringMemento(); + comparator.saveState(EVENTS_TABLE_MEMENTO_ID, m); + } + comparator = new TableComparatorFactory(explorer); + if (m != null) + comparator.restoreState(EVENTS_TABLE_MEMENTO_ID, m); + eval.addComparator(comparator, 2.0); + data.addEvaluator(Object.class, eval); + } + }; + eventExplorer.setBrowseContexts(getBrowseContexts()); + eventExplorer.setContextMenuId(getContextMenuId()); + eventExplorer.setColumns(COLUMNS); + eventExplorer.finish(); + GridDataFactory.fillDefaults().grab(true, true).span(2, 1).applyTo(eventExplorer); + + attachHeaderListeners(eventExplorer); + } + + public static List getEventLogs(ReadGraph graph) throws NoSingleResultException, DoesNotContainValueException, BindingException, ServiceException, DatabaseException { + Layer0X L0X = Layer0X.getInstance(graph); + SimulationResource SIMU = SimulationResource.getInstance(graph); + EventResource EVENT = EventResource.getInstance(graph); + + Resource project = Simantics.getProjectResource(); + + List eventLogs = new ArrayList(); + for (Resource activeModel : graph.syncRequest(new ObjectsWithType(project, L0X.Activates, SIMU.Model))) { + for (Resource eventLog : graph.syncRequest(new ObjectsWithType(activeModel, EVENT.HasEventLog, EVENT.EventLog))) { + if (!graph.hasStatement(eventLog, EVENT.Hidden)) { + eventLogs.add(eventLog); +// graph.getRelatedValue(eventLog, EVENT.HasModificationCounter, Bindings.INTEGER); + } + } + } + return eventLogs; + } + + @Override + public void saveState(IMemento memento) { + super.saveState(memento); + if (comparator != null) + comparator.saveState(EVENTS_TABLE_MEMENTO_ID, memento); + } + + private void attachHeaderListeners(GraphExplorerComposite explorer) { + final Tree tree = explorer.getExplorerControl(); + + String sortColumn = null; + if (comparator != null) { + sortColumn = comparator.getColumn(); + } + + for (final TreeColumn column : tree.getColumns()) { + column.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + Column c = (Column) column.getData(); + String key = transformColumn( c.getKey() ); + + comparator.setColumn(key); + comparator.toggleAscending(key); + comparator.refresh(); + + // Visualizes the selected sorting in the Tree column header + tree.setSortColumn(column); + tree.setSortDirection(comparator.isAscending(key) ? SWT.DOWN : SWT.UP); + } + }); + + Column c = (Column) column.getData(); + String key = transformColumn( c.getKey() ); + if (key.equals(sortColumn)) { + tree.setSortColumn(column); + tree.setSortDirection(comparator.isAscending(key) ? SWT.DOWN : SWT.UP); + } + } + } + + private Class getColumnType(String key) { + if (Constants.COLUMN_EVENT_INDEX.equals(key)) + return Long.class; + if (Constants.COLUMN_TIMESTAMP_NUMERIC.equals(key) + || Constants.COLUMN_RETURN_TIME_NUMERIC.equals(key)) + return Double.class; + return String.class; + } + + private String transformColumn(String key) { + // HACK: for proper numeric timestamp sorting since + // formatted time values don't sort all that well. + if (Constants.COLUMN_TIMESTAMP.equals(key)) + key = Constants.COLUMN_TIMESTAMP_NUMERIC; + if (Constants.COLUMN_RETURN_TIME.equals(key)) + key = Constants.COLUMN_RETURN_TIME_NUMERIC; + return key; + } + + @Override + public void setFocus() { + eventExplorer.setFocus(); + } + + @Override + protected IPropertyPage getPropertyPage() { + return null; + } + + public class TableComparatorFactory implements ComparableContextFactory { + + private static final String SORTING = "sorting"; + private final static String SORT_COL = "sortCol"; + private final static String COLS = "cols"; + private final static String COL_ASCEND = "ascend"; + + UpdateTrigger updateTrigger = new UpdateTrigger(); + + /** Current sort column */ + private String column = null; + /** Ascending state of each column */ + private Map ascendMap = new HashMap(); + + public TableComparatorFactory(GraphExplorer ge) { + ge.setPrimitiveProcessor(updateTrigger); + } + + public void saveState(String id, IMemento memento) { + if (id == null) + throw new NullPointerException("null id"); + IMemento m = null; + for (IMemento child : memento.getChildren(SORTING)) { + if (id.equals(child.getID())) + m = child; + } + if (m == null) + m = memento.createChild(SORTING, id); + if (getColumn() != null) + m.putString(SORT_COL, getColumn()); + + columns: + for (String columnKey : ascendMap.keySet()) { + for (IMemento col : m.getChildren(COLS)) { + if (columnKey.equals(col.getID())) { + col.putBoolean(COL_ASCEND, isAscending(columnKey)); + continue columns; + } + } + IMemento col = m.createChild(COLS, columnKey); + col.putBoolean(COL_ASCEND, isAscending(columnKey)); + } + } + + public void restoreState(String id, IMemento memento) { + if (id == null) + throw new NullPointerException("null id"); + if (!hasState(id, memento)) + return; + for (IMemento m : memento.getChildren(SORTING)) { + if (!id.equals(m.getID())) + continue; + for (IMemento column : m.getChildren(COLS)) { + setAscending( column.getID(), column.getBoolean(COL_ASCEND) ); + } + setColumn( m.getString(SORT_COL) ); + break; + } + } + + public boolean hasState(String id, IMemento memento) { + for (IMemento m : memento.getChildren(SORTING)) + if (id.equals(m.getID())) + return true; + return false; + } + + public void setAscending(String column, Boolean ascending) { + if (ascending == null) + ascendMap.remove(column); + else + ascendMap.put(column, ascending); + } + + public boolean isAscending(String column) { + return !Boolean.FALSE.equals(ascendMap.get(column)); + } + + public void setColumn(String column) { + this.column = column; + } + + public void toggleAscending(String column) { + Boolean ascending = ascendMap.get(column); + if (ascending == null) { + ascendMap.put(column, Boolean.TRUE); + } else { + ascendMap.put(column, !ascending); + } + } + + public void refresh() { + updateTrigger.refresh(); + } + + public String getColumn() { + return column; + } + + @Override + public ComparableContext[] create(NodeQueryManager manager, NodeContext parent, NodeContext[] children) { + // To make this query refreshable. + manager.query(parent, UpdateTrigger.KEY); + + // Don't sort if no sort column is defined + if (column == null) + return null; + + final boolean _ascending = isAscending(column); + final Class _clazz = getColumnType(column); + + ComparableContext[] result = new ComparableContext[children.length]; + for (int i = 0; i < children.length; i++) { + NodeContext child = children[i]; + Labeler labeler = manager.query(child, BuiltinKeys.SELECTED_LABELER); + + int category = (labeler != null) ? labeler.getCategory() : 0; + String label = (labeler != null && column != null) ? labeler.getLabels().get(column) : ""; + if (label == null) + label = ""; + + result[i] = new ImmutableLexicalComparable(category, label, child) { + final boolean ascending = _ascending; + final Class clazz = _clazz; + + @SuppressWarnings("unchecked") + @Override + public int compareTo(ComparableContext arg0) { + ImmutableLexicalComparable other = (ImmutableLexicalComparable) arg0; + + int catDelta = getCategory() - other.getCategory(); + if (!ascending) + catDelta = -catDelta; + if (catDelta != 0) + return catDelta; + + String label1 = getLabel(); + String label2 = other.getLabel(); + + @SuppressWarnings("rawtypes") + Comparator comparator = AlphanumComparator.CASE_INSENSITIVE_COMPARATOR; + if (clazz == Double.class) { + comparator = DoubleStringComparator.INSTANCE; + } else if (clazz == Long.class) { + comparator = LongStringComparator.INSTANCE; + } + + return ascending ? comparator.compare(label1, label2) + : comparator.compare(label2, label1); + } + }; + } + + return result; + } + + @Override + public String toString() { + return "Event Table Sort"; + } + + } + + private static class LongStringComparator implements Comparator { + + private static final LongStringComparator INSTANCE = new LongStringComparator(); + + @Override + public int compare(String s1, String s2) { + long n1 = parseLong(s1); + long n2 = parseLong(s2); + return compare(n1, n2); + } + + private long parseLong(String s) { + try { + return Long.parseLong(s); + } catch (NumberFormatException e1) { + return Long.MIN_VALUE; + } + } + + public static int compare(long x, long y) { + return (x < y) ? -1 : ((x == y) ? 0 : 1); + } + } + + private static class DoubleStringComparator implements Comparator { + + private static final DoubleStringComparator INSTANCE = new DoubleStringComparator(); + + @Override + public int compare(String s1, String s2) { + double n1 = parseDouble(s1); + double n2 = parseDouble(s2); + return Double.compare(n1, n2); + } + + private double parseDouble(String s) { + try { + return Double.parseDouble(s); + } catch (NumberFormatException e2) { + return Double.NaN; + } + } + } + + public static class UpdateTrigger extends AbstractPrimitiveQueryProcessor implements ProcessorLifecycle { + + private Double random = Math.random(); + private Map contexts = new THashMap(); + + public static final PrimitiveQueryKey KEY = new PrimitiveQueryKey() { + @Override + public String toString() { + return "TRIGGER"; + } + }; + + @Override + public Object getIdentifier() { + return KEY; + } + + public void refresh() { + random = Math.random(); + for (Map.Entry entry : contexts.entrySet()) { + entry.getValue().scheduleReplace(entry.getKey(), KEY, random); + } + } + + @Override + public Double query(PrimitiveQueryUpdater updater, NodeContext context, PrimitiveQueryKey key) { + this.contexts.put(context, updater); + return random; + } + + @Override + public String toString() { + return "UpdateTrigger"; + } + + @Override + public void attached(GraphExplorer explorer) { + } + + @Override + public void detached(GraphExplorer explorer) { + clear(); + } + + @Override + public void clear() { + contexts.clear(); + } + + } + +}