Expose acquire/release stack traces by double clicking on Cache view (fixes #533744)
diff --git a/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/AlphabeticalSorter.java b/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/AlphabeticalSorter.java
deleted file mode 100644
index 555a923..0000000
--- a/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/AlphabeticalSorter.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*******************************************************************************

- * Copyright (c) 2008 The University of York.

- * 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:

- *     Dimitrios Kolovos - initial API and implementation

- ******************************************************************************/

-package org.eclipse.epsilon.dt.epackageregistryexplorer;

-

-import org.eclipse.jface.viewers.ContentViewer;

-import org.eclipse.jface.viewers.IBaseLabelProvider;

-import org.eclipse.jface.viewers.ILabelProvider;

-import org.eclipse.jface.viewers.Viewer;

-import org.eclipse.jface.viewers.ViewerSorter;

-

-public class AlphabeticalSorter extends ViewerSorter 

-{ 

-	

-    @Override

-	public int category(Object element) {

-    	return 0;

-    }

-

-    @Override

-	public int compare(Viewer viewer, Object e1, Object e2) {

-		String name1, name2;

-		if (viewer == null || !(viewer instanceof ContentViewer)) {

-			name1 = e1.toString();

-			name2 = e2.toString();

-		} 

-		else {

-			IBaseLabelProvider prov = ((ContentViewer) viewer).getLabelProvider();

-			if (prov instanceof ILabelProvider) {

-				ILabelProvider lprov = (ILabelProvider) prov;

-				name1 = lprov.getText(e1);

-				name2 = lprov.getText(e2);

-			} 

-			else {

-				name1 = e1.toString();

-				name2 = e2.toString();

-			}

-		}

-		if (name1 == null)

-			name1 = "";

-		if (name2 == null)

-			name2 = "";

-		return collator.compare(name1, name2);

-	}

-    

-    

-}

diff --git a/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/CacheItemDialog.java b/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/CacheItemDialog.java
new file mode 100644
index 0000000..deea505
--- /dev/null
+++ b/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/CacheItemDialog.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Aston University.
+ * 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:
+ *     Antonio Garcia-Dominguez - initial API and implementation
+******************************************************************************/
+package org.eclipse.epsilon.dt.epackageregistryexplorer;
+
+import org.eclipse.epsilon.emc.emf.CachedResourceSet.Cache.CacheItem;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+public class CacheItemDialog extends TitleAreaDialog {
+
+	private static final class StackTraceLabelProvider extends LabelProvider {
+		@Override
+		public String getText(Object element) {
+			StackTraceElement[] stackTrace = (StackTraceElement[]) element;
+			StringBuilder sb = new StringBuilder();
+			for (StackTraceElement elem : stackTrace) { 
+				sb.append(elem.toString());
+				sb.append('\n');
+			}
+			return sb.toString();
+		}
+	}
+
+	private final CacheItem cItem;
+
+	public CacheItemDialog(Shell shell, CacheItem cItem) {
+		super(shell);
+		this.cItem = cItem;
+	}
+
+	@Override
+	protected Control createDialogArea(Composite parent) {
+		final Composite area = (Composite) super.createDialogArea(parent);
+
+		final Composite container = new Composite(area, SWT.NONE);
+		container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+		final GridLayout containerLayout = new GridLayout();
+		container.setLayout(containerLayout);
+		containerLayout.numColumns = 1;
+
+		final Label lblCheckedIn = new Label(container, SWT.NONE);
+		lblCheckedIn.setText("Checked in from:");
+		
+		final ListViewer lstCheckedIn = new ListViewer(container);
+		lstCheckedIn.setContentProvider(new ArrayContentProvider());
+		lstCheckedIn.setLabelProvider(new StackTraceLabelProvider());
+		lstCheckedIn.setInput(cItem.getCheckedInFrom().toArray());
+
+		final Label lblCheckedOut = new Label(container, SWT.NONE);
+		lblCheckedOut.setText("Checked out from:");
+		final ListViewer lstCheckedOut = new ListViewer(container);
+		lstCheckedOut.setContentProvider(new ArrayContentProvider());
+		lstCheckedOut.setLabelProvider(new StackTraceLabelProvider());
+		lstCheckedOut.setInput(cItem.getCheckedOutFrom().toArray());
+
+		return area;
+	}
+
+	@Override
+	public void create() {
+		super.create();
+		setTitle("Cache item details");
+		setMessage("Check where the cache item was acquired and released from");
+	}
+
+}
diff --git a/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/CachedResourceView.java b/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/CachedResourceView.java
index 1cbd74f..a35cdc6 100644
--- a/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/CachedResourceView.java
+++ b/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/CachedResourceView.java
@@ -1,45 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2014-2018 University of York, Aston University.
+ * 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:
+ *     Dimitris Kolovos - initial API and implementation
+ *     Antonio Garcia-Dominguez - double click to see acquired/released stack traces
+******************************************************************************/
 package org.eclipse.epsilon.dt.epackageregistryexplorer;
 
 import org.eclipse.epsilon.emc.emf.CachedResourceSet;
 import org.eclipse.epsilon.emc.emf.CachedResourceSet.Cache;
 import org.eclipse.epsilon.emc.emf.CachedResourceSet.Cache.CacheItem;
 import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
 import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.ITableLabelProvider;
 import org.eclipse.jface.viewers.LabelProvider;
 import org.eclipse.jface.viewers.TableViewer;
 import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.TableColumn;
 import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.part.ViewPart;
 
 public class CachedResourceView extends ViewPart {
-	
+
 	protected TableViewer cacheViewer = null;
-	
+
 	@Override
 	public void createPartControl(Composite parent) {
 		cacheViewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
 		cacheViewer.setContentProvider(new CachedResourceViewerContentProvider());
 		cacheViewer.setLabelProvider(new CachedResoureViewerLabelProvider());
-		cacheViewer.setSorter(new AlphabeticalSorter());
+		cacheViewer.setComparator(new ViewerComparator((o1, o2) -> o1.compareTo(o2)));
 		cacheViewer.setInput(CachedResourceSet.getCache());
-		
+		cacheViewer.addDoubleClickListener(new IDoubleClickListener() {
+			@Override
+			public void doubleClick(DoubleClickEvent event) {
+				IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+				CacheItem cItem = (CacheItem) sel.getFirstElement();
+				new CacheItemDialog(
+						PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
+						cItem).open();
+			}
+		});
+
 		TableColumn column = new TableColumn(cacheViewer.getTable(), SWT.FULL_SELECTION);
-	    column.setText("Resource");
-	    column.setWidth(300);
-	    
-	    column = new TableColumn(cacheViewer.getTable(), SWT.FULL_SELECTION);
-	    column.setText("Garbage-collected");
-	    column.setWidth(120);
-		
-	    cacheViewer.getTable().setHeaderVisible(true);
-	    cacheViewer.getTable().setLinesVisible(true);
-	    
-	    
+		column.setText("Resource");
+		column.setWidth(300);
+
+		column = new TableColumn(cacheViewer.getTable(), SWT.FULL_SELECTION);
+		column.setText("Garbage-collected");
+		column.setWidth(120);
+
+		cacheViewer.getTable().setHeaderVisible(true);
+		cacheViewer.getTable().setLinesVisible(true);
+
 		IActionBars bars = getViewSite().getActionBars();
 		bars.getToolBarManager().add(new RefreshAction());
 		bars.getToolBarManager().add(new ClearAction());
@@ -47,72 +72,74 @@
 
 	@Override
 	public void setFocus() {
-		
+
 	}
-	
+
 	class RefreshAction extends Action {
-		
+
 		public RefreshAction() {
 			this.setText("Refresh");
 			this.setImageDescriptor(Activator.getDefault().getImageDescriptor("icons/refresh.gif"));
 		}
-		
+
 		@Override
 		public void run() {
 			cacheViewer.refresh();
-		}		
+		}
 	}
-	
+
 	class ClearAction extends Action {
-		
+
 		public ClearAction() {
 			this.setText("Clear cache");
 			this.setImageDescriptor(Activator.getDefault().getImageDescriptor("icons/clear.gif"));
 		}
-		
+
 		@Override
 		public void run() {
 			CachedResourceSet.getCache().clear();
 			cacheViewer.refresh();
-		}		
+		}
 	}
-	
+
 	public class CachedResoureViewerLabelProvider extends LabelProvider implements ITableLabelProvider {
-		
+
 		protected Image image = Activator.getDefault().getImageDescriptor("icons/emfmodel.gif").createImage();
-		
 
 		@Override
 		public Image getColumnImage(Object element, int columnIndex) {
-			if (columnIndex == 0) return image;
+			if (columnIndex == 0)
+				return image;
 			return null;
 		}
 
 		@Override
 		public String getColumnText(Object element, int columnIndex) {
-			if (columnIndex == 0) return ((CacheItem) element).getUri().toString();
-			else return (((CacheItem) element).getResource() == null) + "";
+			if (columnIndex == 0)
+				return ((CacheItem) element).getUri().toString();
+			else
+				return (((CacheItem) element).getResource() == null) + "";
 		}
-		
+
 	}
-	
+
 	public class CachedResourceViewerContentProvider implements IStructuredContentProvider {
 
 		@Override
 		public void dispose() {
-			
+
 		}
 
 		@Override
 		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
-			
+
 		}
 
 		@Override
 		public Object[] getElements(Object inputElement) {
-			return CachedResourceSet.getCache().getItems().toArray(new Cache.CacheItem[]{});
+			return CachedResourceSet.getCache().getItems().toArray(new Cache.CacheItem[] {});
 		}
-		
+
 	}
-	
+
 }
diff --git a/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/PackageRegistryExplorerView.java b/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/PackageRegistryExplorerView.java
index c120346..a3a3b92 100644
--- a/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/PackageRegistryExplorerView.java
+++ b/plugins/org.eclipse.epsilon.dt.epackageregistryexplorer/src/org/eclipse/epsilon/dt/epackageregistryexplorer/PackageRegistryExplorerView.java
@@ -37,6 +37,7 @@
 import org.eclipse.jface.viewers.TreeSelection;

 import org.eclipse.jface.viewers.TreeViewer;

 import org.eclipse.jface.viewers.Viewer;

+import org.eclipse.jface.viewers.ViewerComparator;

 import org.eclipse.swt.SWT;

 import org.eclipse.swt.custom.CLabel;

 import org.eclipse.swt.custom.SashForm;

@@ -126,7 +127,7 @@
 		classViewer.setContentProvider(new PackageRegistryContentProvider(this));

 		classViewer.setLabelProvider(eCoreLabelProvider);

 		classViewer.addSelectionChangedListener(new ClassViewerSelectionChangedListener());

-		classViewer.setSorter(new AlphabeticalSorter());

+		classViewer.setComparator(new ViewerComparator((o1, o2) -> o1.compareTo(o2)));

 		classViewer.setInput(getViewSite());

 		getSite().setSelectionProvider(this);		

 		

@@ -165,7 +166,7 @@
 		featureViewerForm.setContent(featureViewer.getControl());

 		featureViewer.setContentProvider(new FeatureViewerContentProvider(this));

 		featureViewer.setLabelProvider(eCoreLabelProvider);

-		featureViewer.setSorter(new AlphabeticalSorter());

+		featureViewer.setComparator(new ViewerComparator((o1, o2) -> o1.compareTo(o2)));

 		featureViewer.setInput(null);

 		featureViewer.addDoubleClickListener(new IDoubleClickListener() {

 

diff --git a/plugins/org.eclipse.epsilon.emc.emf/src/org/eclipse/epsilon/emc/emf/CachedResourceSet.java b/plugins/org.eclipse.epsilon.emc.emf/src/org/eclipse/epsilon/emc/emf/CachedResourceSet.java
index b201374..91af0f8 100644
--- a/plugins/org.eclipse.epsilon.emc.emf/src/org/eclipse/epsilon/emc/emf/CachedResourceSet.java
+++ b/plugins/org.eclipse.epsilon.emc.emf/src/org/eclipse/epsilon/emc/emf/CachedResourceSet.java
@@ -2,6 +2,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 import org.eclipse.emf.common.util.URI;
@@ -126,9 +127,16 @@
 		}
 		
 		public class CacheItem {
-			protected WeakReference<Resource> resourceReference;
-			public URI uri;
-			public int checkedOut = 0;
+			private WeakReference<Resource> resourceReference;
+			private URI uri;
+
+			/**
+			 * Track where increments/decrements were done from, to help developers
+			 * and integrators track leaks.
+			 */
+			private List<StackTraceElement[]> checkedInFrom = new ArrayList<>();
+			private List<StackTraceElement[]> checkedOutFrom = new ArrayList<>();
+			private int checkedOutCount = 0;
 			
 			public Resource getResource() {
 				return resourceReference.get();
@@ -147,17 +155,26 @@
 			}
 			
 			public void incrementCheckedOut() {
-				checkedOut ++;
+				checkedInFrom.add(Thread.currentThread().getStackTrace());
+				checkedOutCount++;
 			}
 			
 			public void decrementCheckedOut() {
-				checkedOut--;
+				checkedOutFrom.add(Thread.currentThread().getStackTrace());
+				checkedOutCount--;
 			}
 			
 			public int getCheckedOut() {
-				return checkedOut;
+				return checkedOutCount;
+			}
+
+			public List<StackTraceElement[]> getCheckedInFrom() {
+				return checkedInFrom;
 			}
 			
+			public List<StackTraceElement[]> getCheckedOutFrom() {
+				return checkedOutFrom;
+			}
 		}
 	}
 }