blob: 0b9c0eeab502c482c34ad9c18b73672f302f57a8 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006, 2015 THALES GLOBAL SERVICES.
* 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:
* Thales - initial API and implementation
*****************************************************************************/
package org.eclipse.amalgam.explorer.contextual.core.provider;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.amalgam.explorer.contextual.core.ExplorerActivator;
import org.eclipse.amalgam.explorer.contextual.core.category.CategoryRegistry;
import org.eclipse.amalgam.explorer.contextual.core.category.ICategory;
import org.eclipse.amalgam.explorer.contextual.core.model.IExplorerContextualModel;
import org.eclipse.amalgam.explorer.contextual.core.provider.wrapper.ExplorerElementWrapper;
import org.eclipse.amalgam.explorer.contextual.core.provider.wrapper.CategoryWrapper;
import org.eclipse.amalgam.explorer.contextual.core.provider.wrapper.EObjectWrapper;
import org.eclipse.amalgam.explorer.contextual.core.query.impl.QueryAdapter;
import org.eclipse.amalgam.explorer.contextual.core.util.ViewerHelper;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.provider.IViewerNotification;
import org.eclipse.emf.edit.provider.ViewerNotification;
import org.eclipse.emf.transaction.ResourceSetChangeEvent;
/**
* Controller.
*/
public abstract class AbstractContentProvider extends AbstractGroupedAdapterFactoryContentProvider implements IExplorerContentProvider {
/**
* Root element i.e the element given as initial input.
*/
protected EObject _rootElement;
/**
* Flag that indicates input has changed.
*/
protected boolean _inputHasChanged;
/**
* Weak hash map for seeking semantic container of an element (category or object)
*/
protected HashMap<ExplorerElementWrapper, ExplorerElementWrapper> _semanticParentHashMap;
protected boolean refreshRequired = false;
protected IExplorerContextualModel model;
/**
* Constructor.
*/
public AbstractContentProvider(AdapterFactory adapterFactory_p, IExplorerContextualModel model_p) {
super(adapterFactory_p);
model = model_p;
_semanticParentHashMap = new HashMap<ExplorerElementWrapper, ExplorerElementWrapper>(0);
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
_semanticParentHashMap.clear();
super.dispose();
}
/**
* Get category children.
* @param wrapper
* @param element
* @param gatheredElements
* @param gatheredCategories
*/
protected void getCategoryChildren(ICategory category_p, ExplorerElementWrapper wrapper, Set<Object> gatheredElements) {
// lookup for the element that we need to query on.
EObject elementToQuery = lookUpEObject(wrapper);
// Gather subCategories & compute queries attached to the category.
gatheredElements.addAll(category_p.compute(elementToQuery));
gatheredElements.addAll(CategoryRegistry.getInstance().gatherSubCategories(getExplorerId(), elementToQuery, category_p));
}
/**
* {@inheritDoc}
*/
@Override
public Object[] getChildren(Object parentElement) {
Object[] result = new Object[0];
try {
if (parentElement instanceof ExplorerElementWrapper) {
ExplorerElementWrapper wrapper = (ExplorerElementWrapper) parentElement;
// retrieve referenced element by the wrapper.
Object element = ((ExplorerElementWrapper) parentElement).getElement();
Set<Object> gatheredElements = new HashSet<Object>(0);
if (wrapper instanceof EObjectWrapper) {
// Provide the root element to the CurrentElement Explorer in purpose to display it.
if ((element == _rootElement) && _inputHasChanged) {
// Root element has no parent : store null value.
_semanticParentHashMap.put(wrapper, null);
_inputHasChanged = false;
if (getExplorerId().equalsIgnoreCase(IExplorerContentProvider.ID_CURRENT_CP)) {
return new Object[] { wrapper };
}
}
getElementChildren((EObject) element, wrapper, gatheredElements);
} else if (wrapper instanceof CategoryWrapper) {
getCategoryChildren((ICategory) element, wrapper, gatheredElements);
}
/**
* Wrap gathered elements & register each wrapper in cache.
*/
Set<Object> wrappers = new HashSet<Object>(0);
for (Object gatherElement : gatheredElements) {
// ignore queries result that returns a null object (reference with cardinality max 1)
if (gatherElement != null) {
ExplorerElementWrapper elementWrapper = wrapElement(gatherElement);
// Add wrapper and element wrapper in internal data and returned collection.
wrappers.add(elementWrapper);
_semanticParentHashMap.put(elementWrapper, wrapper);
// Flag to filter out empty category.
boolean shouldRemovedEmptyCategoryWrapper = false;
if (gatherElement instanceof ICategory) {
if (!model.doesShowCategory((ICategory) gatherElement)) {
shouldRemovedEmptyCategoryWrapper = true;
} else {
Set<Object> categoryChildren = new HashSet<Object>(0);
// Compute category children, if no child, remove this category from displayed elements.
getCategoryChildren((ICategory) gatherElement, elementWrapper, categoryChildren);
if (categoryChildren.isEmpty()) {
shouldRemovedEmptyCategoryWrapper = true;
}
}
}
if (shouldRemovedEmptyCategoryWrapper) {
wrappers.remove(elementWrapper);
_semanticParentHashMap.remove(elementWrapper);
}
}
}
result = wrappers.toArray();
} else {
// Wrap given element. This input element can't be a Category because a category element is computed.
result = getChildren(new EObjectWrapper(parentElement));
}
} catch (Exception exception_p) {
ExplorerActivator.getDefault().getLog()
.log(new Status(IStatus.ERROR, ExplorerActivator.PLUGIN_ID, "Error while getting children for " + parentElement, exception_p)); //$NON-NLS-1$
result = new Object[0];
}
return result;
}
/**
* Ge element children.
* @param wrapper
* @param gatheredElements
* @param gatheredCategories
* @param eObject
*/
protected void getElementChildren(EObject eObject, ExplorerElementWrapper wrapper, Set<Object> gatheredElements) {
if (eObject != _rootElement) {
// If it's an item gather sub-queries from parent category.
ExplorerElementWrapper semanticParentWrapper = _semanticParentHashMap.get(wrapper);
if (null != semanticParentWrapper) {
Object semanticParent = semanticParentWrapper.getElement();
if (semanticParent instanceof ICategory) {
// temporary result (list typed)
for (Object query : ((ICategory) semanticParent).getItemQueries()) {
// compute item queries with eobject as context.
gatheredElements.addAll(QueryAdapter.getInstance().compute(eObject, query));
}
}
}
}
// Gather direct categories for any eobject.
ExplorerElementWrapper parentWrapper = _semanticParentHashMap.get(wrapper);
if (null == _semanticParentHashMap.get(parentWrapper)) // for blocking recursion
{
gatheredElements.addAll(CategoryRegistry.getInstance().gatherCategories(getExplorerId(), eObject));
}
}
/**
* {@inheritDoc}
*/
@Override
public Object[] getElements(Object rootElement_p) {
return getChildren(rootElement_p);
}
/**
* {@inheritDoc}
*/
@Override
public Object getParent(Object element_p) {
return _semanticParentHashMap.get(element_p);
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasChildren(Object element_p) {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void inputChanged(Viewer viewer_p, Object oldInput, Object newInput) {
if (null == viewer) {
viewer = viewer_p;
}
if (newInput instanceof ExplorerElementWrapper) {
if (oldInput == null) {
inputChanged(viewer_p, new EObjectWrapper(null), ((ExplorerElementWrapper) newInput).getElement());
} else if (oldInput instanceof ExplorerElementWrapper) {
inputChanged(viewer_p, ((ExplorerElementWrapper) oldInput).getElement(), ((ExplorerElementWrapper) newInput).getElement());
} else if (oldInput instanceof EObject) {
inputChanged(viewer_p, oldInput, ((ExplorerElementWrapper) newInput).getElement());
}
} else if (newInput instanceof EObject) {
// clear cache.
_semanticParentHashMap.clear();
_inputHasChanged = true;
_rootElement = (EObject) newInput;
} else if (null == newInput) {
// View is closing or no input selection.
_inputHasChanged = false;
_rootElement = null;
_semanticParentHashMap.clear();
}
}
/**
* Look up an eobject for specified wrapper.
* @param wrapper_p
* @return
*/
private EObject lookUpEObject(ExplorerElementWrapper wrapper_p) {
ExplorerElementWrapper parentWrapper = _semanticParentHashMap.get(wrapper_p);
if (parentWrapper instanceof CategoryWrapper) {
return lookUpEObject(parentWrapper);
}
return (EObject) parentWrapper.getElement();
}
@Override
protected boolean refreshRequired(ResourceSetChangeEvent event_p) {
boolean result = false;
synchronized (this) {
if (refreshRequired) {
result = true;
}
}
// TODO bring back the code of the method initially available in GroupedAdapterFactoryContentProvider.
// This code has dependencies to Sirius, So find a way to break this link or move this class to UI plugins.
return result /*|| super.refreshRequired(event_p)*/;
}
/**
* Overridden to forward EObject notifications to wrapped equivalent objects.<br>
* {@inheritDoc}
*/
@Override
public void notifyChanged(Notification notification_p) {
if (notification_p instanceof IViewerNotification) {
final IViewerNotification viewerNotification = (IViewerNotification) notification_p;
if (!viewerNotification.isContentRefresh()) {
Object element = viewerNotification.getElement();
Object wrappedElement = wrapElement(element);
if (null != wrappedElement) {
super.notifyChanged(ViewerNotification.wrapNotification(viewerNotification, wrappedElement));
}
} else {
// Impossible to know if modifications from other AbstractContentProvider instances in the Contextual Explorer have impacts on current one.
// Let's refresh completely the viewer.
// refresh fails here due to the poor content provider implementation based on EObjectWrapper & co.
synchronized (this) {
if (!refreshRequired) {
refreshRequired = true;
}
}
}
}
}
@Override
protected void processRefresh() {
boolean refresh = false;
// If we need to refresh complete view, we refresh it and don't perform any refresh for all others notifications
synchronized (this) {
refresh = refreshRequired;
refreshRequired = false;
if (refreshRequired) {
notifications = null;
toRefresh = null;
}
}
if (!refresh) {
super.processRefresh();
} else {
if ((viewer != null) && (viewer.getControl() != null) && !viewer.getControl().isDisposed()) {
ViewerHelper.run((StructuredViewer) viewer, new Runnable() {
@SuppressWarnings("synthetic-access")
public void run() {
Object input = viewer.getInput();
viewer.setInput(input);
}
});
}
}
}
/**
* Wrap element in the proper wrapper.
* @param gatherElement_p
* @return
*/
private ExplorerElementWrapper wrapElement(Object gatherElement_p) {
ExplorerElementWrapper wrapper = null;
if ((gatherElement_p != null) && (gatherElement_p instanceof EObject)) {
wrapper = new EObjectWrapper(gatherElement_p);
} else if (gatherElement_p instanceof ICategory) {
wrapper = new CategoryWrapper(gatherElement_p);
}
return wrapper;
}
/**
* Get root element.
* @return
*/
public EObject getRootElement() {
return _rootElement;
}
}