/* Copyright (C) 2011 by Lars Schuetze (lschuetze@gmx.net) This file is part of the OCL 2 Interpreter of Dresden OCL2 for Eclipse. Dresden OCL2 for Eclipse is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Dresden OCL2 for Eclipse is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Dresden OCL2 for Eclipse. If not, see <http://www.gnu.org/licenses/>. */ package org.dresdenocl.tracer.ui.internal.views; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.WeakHashMap; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.part.ViewPart; import org.dresdenocl.essentialocl.standardlibrary.OclAny; import org.dresdenocl.interpreter.OclInterpreterPlugin; import org.dresdenocl.interpreter.event.IInterpreterTraceListener; import org.dresdenocl.interpreter.event.internal.InterpreterTraceEvent; import org.dresdenocl.modelinstancetype.types.IModelInstanceElement; import org.dresdenocl.pivotmodel.Constraint; import org.dresdenocl.tracer.tracermodel.TracerItem; import org.dresdenocl.tracer.tracermodel.TracerRoot; import org.dresdenocl.tracer.tracermodel.TracermodelFactory; import org.dresdenocl.tracer.tracermodel.impl.TracermodelPackageImpl; import org.dresdenocl.tracer.tracermodel.provider.TracermodelItemProviderAdapterFactory; import org.dresdenocl.tracer.ui.TracerUIPlugin; import org.dresdenocl.tracer.ui.actions.TracerViewMenuAction; import org.dresdenocl.tracer.ui.actions.TracerViewMenuActionType; import org.dresdenocl.tracer.ui.internal.msg.OclTracerUIMessages; import org.dresdenocl.tracer.ui.internal.views.util.TracerItemAdapterFactoryContentProvider; import org.dresdenocl.tracer.ui.internal.views.util.TracerItemAdapterFactoryLabelProvider; import org.dresdenocl.tracer.ui.internal.views.util.TracerItemViewerFilter; import org.dresdenocl.tracer.ui.internal.views.util.ViewerFilterType; /** * @author Lars Schuetze * */ public class TracerView extends ViewPart implements IInterpreterTraceListener { /** Icon to clear the view. */ private static String CLEAR_IMAGE = "icons/clear.gif"; /** The {@link TreeViewer} for this {@link TracerView}. */ private TreeViewer myTreeViewer; /** The {@link TracerRoot} of the tree. */ private TracerRoot tracerRoot; /** The {@link ComposedAdapterFactory} of the {@link tracer}. */ private ComposedAdapterFactory myAdapterFactory; /** The {@link IMenuManager} of this {@link TracerView}. */ private IToolBarManager myToolBarManager; /** The {@link IMenuManager} of this {@link TracerView}. */ private IMenuManager myMenuManager; /** The {@link TracerItemViewerFilter} of this {@link TreeViewer} */ private TracerItemViewerFilter myViewerFilter; /** Pointing to the current parent */ private TracerItem currentParent; /** The factory to create new instances */ private TracermodelFactory factory; /** This map holds the TracerItems in a cache for fast access */ private WeakHashMap<UUID, TracerItem> cachedItems; /** * <p> * Instantiates this view. * </p> */ @SuppressWarnings("unchecked") public TracerView() { super(); TracermodelPackageImpl.init(); factory = TracermodelFactory.eINSTANCE; List<AdapterFactory> factories = new ArrayList<AdapterFactory>(); Collections.addAll(factories, new TracermodelItemProviderAdapterFactory(), new ReflectiveItemProviderAdapterFactory(), new ResourceItemProviderAdapterFactory()); myAdapterFactory = new ComposedAdapterFactory(factories); cachedItems = new WeakHashMap<UUID, TracerItem>(); tracerRoot = factory.createTracerRoot(); currentParent = null; // Add this listener to the InterpreterRegistry OclInterpreterPlugin.getInterpreterRegistry().addInterpreterTraceListener( this); } @Override public void dispose() { try { OclInterpreterPlugin.getInterpreterRegistry() .removeInterpreterTraceListener(this); myAdapterFactory.dispose(); } finally { super.dispose(); } } @Override public void createPartControl(Composite parent) { // This tree defines the layout for the TreeView Tree myTracerTree = new Tree(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); myTracerTree.setHeaderVisible(true); myTracerTree.setLinesVisible(true); TreeColumn column1 = new TreeColumn(myTracerTree, 0); column1.setText("Constraint"); column1.setWidth(400); TreeColumn column2 = new TreeColumn(myTracerTree, 0); column2.setText("Result"); column2.setWidth(200); myViewerFilter = new TracerItemViewerFilter(); myTreeViewer = new TreeViewer(myTracerTree); myTreeViewer.addFilter(myViewerFilter); myTreeViewer .setContentProvider(new TracerItemAdapterFactoryContentProvider( myAdapterFactory)); myTreeViewer.setLabelProvider(new TracerItemAdapterFactoryLabelProvider( myAdapterFactory)); myTreeViewer.setInput(tracerRoot); initMenu(); } /** * <p> * Creates the menu of this {@link TracerView}. * </p> */ private void initMenu() { TracerViewMenuAction clearAllTracedElements; TracerViewMenuAction filterFalseElements; TracerViewMenuAction filterTrueElements; TracerViewMenuAction filterNothing; TracerViewMenuAction removeSelection; /* * --- TOOLBAR */ /* Add the clear all traced elements button to the tool bar. */ { clearAllTracedElements = new TracerViewMenuAction( TracerViewMenuActionType.CLEAR_ALL_ELEMTENTS, this); clearAllTracedElements.setImageDescriptor(TracerUIPlugin .getImageDescriptor(CLEAR_IMAGE)); clearAllTracedElements.setText(OclTracerUIMessages.TracerView_Menu_Clear); this.getToolBarManager().add(clearAllTracedElements); } /* * --- ACTION BAR */ /* Add all menu items to the drop down menu. */ { removeSelection = new TracerViewMenuAction(TracerViewMenuActionType.REMOVE_SELECTION, this); removeSelection .setText(OclTracerUIMessages.TracerView_Remove_Selection_Title); this.getMenuManager().add(removeSelection); filterNothing = new TracerViewMenuAction(TracerViewMenuActionType.FILTER_NOTHING, this); filterNothing .setText(OclTracerUIMessages.TracerView_Filter_Nothing_Title); this.getMenuManager().add(filterNothing); filterFalseElements = new TracerViewMenuAction( TracerViewMenuActionType.FILTER_FALSE_ELEMENTS, this); filterFalseElements .setText(OclTracerUIMessages.TracerView_Filter_False_Title); this.getMenuManager().add(filterFalseElements); filterTrueElements = new TracerViewMenuAction( TracerViewMenuActionType.FILTER_TRUE_ELEMENTS, this); filterTrueElements .setText(OclTracerUIMessages.TracerView_Filter_True_Title); this.getMenuManager().add(filterTrueElements); } } /** * <p> * This method encapsulate the call to the tool bar manager. * </p> * * @return The {@link IToolBarManager} of the * {@link org.eclipse.ui.part.ViewPart ViewPart}. */ private IToolBarManager getToolBarManager() { if (myToolBarManager == null) { myToolBarManager = this.getViewSite().getActionBars().getToolBarManager(); } return myToolBarManager; } /** * <p> * This method encapsulate the call to the tool bar manager. * </p> * * @return The {@link IMenuManager} of the {@link ViewPart}. */ private IMenuManager getMenuManager() { if (myMenuManager == null) { myMenuManager = this.getViewSite().getActionBars().getMenuManager(); } return myMenuManager; } @Override public void setFocus() { myTreeViewer.getControl().setFocus(); } /** * <p> * Clears the view and all its content. * </p> */ public synchronized void clearTracerView() { // TODO: check for memory leaks synchronized (tracerRoot) { cachedItems.clear(); if (tracerRoot.getRootItems() != null) { Deque<TracerItem> stack = new ArrayDeque<TracerItem>(tracerRoot.getRootItems()); while (!stack.isEmpty()) { TracerItem item = stack.pop(); if (item.getChildren() != null) { stack.addAll(item.getChildren()); item.getChildren().clear(); } // no else } tracerRoot.getRootItems().clear(); } // no else } // end synchronized setFilterElements(cachedItems); } /** * <p> * Sets the {@link TracerItemViewerFilter} to filter the given * {@link ViewerFilterType}. * </p> * * @param filterType * the {@link ViewerFilterType} to be set */ public void setFilterType(ViewerFilterType filterType) { if (filterType != null) { myViewerFilter.setFilterType(filterType); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { myTreeViewer.refresh(false); } }); } // no else } public void setFilterElements(Map<UUID, TracerItem> map) { if (map == null) { myViewerFilter.setFilterElements(cachedItems); } else { myViewerFilter.setFilterElements(map); } Display.getDefault().syncExec(new Runnable() { @Override public void run() { myTreeViewer.refresh(false); } }); } @Override public synchronized void interpretationTreeDepthIncreased(UUID uuid) { TracerItem dummyItem = factory.createTracerItem(); dummyItem.setUUID(uuid); synchronized (tracerRoot) { if (currentParent == null) { tracerRoot.getRootItems().add(dummyItem); } else { currentParent.getChildren().add(dummyItem); } } // end synchronized cachedItems.put(uuid, dummyItem); dummyItem.setParent(currentParent); currentParent = dummyItem; } @Override public synchronized void interpretationTreeDepthIncreased(UUID uuid, IModelInstanceElement modelInstanceElement) { interpretationTreeDepthIncreased(uuid); cachedItems.get(uuid).setModelInstanceElement(modelInstanceElement); } @Override public synchronized void interpretationTreeDepthDecreased() { if (currentParent != null) { currentParent = currentParent.getParent(); if (currentParent == null) { setFilterElements(cachedItems); } // no else } // no else } @Override public synchronized void partialInterpretationFinished( InterpreterTraceEvent event) { if (event != null) { TracerItem item = cachedItems.get(event.getUUID()); if (item != null) { item.setExpression(event.getExpression()); item.setResult(event.getResult()); } // no else } // no else } @Override public synchronized void interpretationCleared() { clearTracerView(); } @Override public synchronized void traceSelectedConstraints(List<Object[]> constraints) { Map<UUID, TracerItem> whiteList = new WeakHashMap<UUID, TracerItem>(); synchronized (tracerRoot) { for (Object[] aRow : constraints) { // Check if aRow has at least three values if (aRow.length >= 3) { // Make sure all types are correct IModelInstanceElement _miElement = null; Constraint _constraint = null; OclAny _result = null; if (aRow[0] instanceof IModelInstanceElement) { _miElement = (IModelInstanceElement) aRow[0]; } if (aRow[1] instanceof Constraint) { _constraint = (Constraint) aRow[1]; } if (aRow[2] instanceof OclAny) { _result = (OclAny) aRow[2]; } if (_miElement != null && _constraint != null && _result != null) { for (TracerItem item : tracerRoot.getRootItems()) { if ((item.getModelInstanceElement() == _miElement) && (item.getExpression() == _constraint) && (item.getResult() == _result)) { whiteList.put(item.getUUID(), item); whiteList.putAll(flatTracerStructure(item.getChildren())); } // no else } // end for } // no else (since one or more values or null) } // no else (aRow has less than three values) } // end for } // end synchronized setFilterElements(whiteList); } private Map<UUID, TracerItem> flatTracerStructure(List<TracerItem> items) { if (items != null) { Map<UUID, TracerItem> result = new WeakHashMap<UUID, TracerItem>(); Deque<TracerItem> stack = new ArrayDeque<TracerItem>(items); while (!stack.isEmpty()) { TracerItem item = stack.pop(); result.put(item.getUUID(), item); if (item.getChildren() != null) { stack.addAll(item.getChildren()); } // no else } // end while return result; } return Collections.emptyMap(); } }