| /******************************************************************************* |
| * Copyright (c) 2016 Obeo. |
| * 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: |
| * Obeo - initial API and implementation and/or initial documentation |
| * ... |
| *******************************************************************************/ |
| package org.eclipse.intent.mapping.ide.ui.view; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.intent.mapping.base.ILink; |
| import org.eclipse.intent.mapping.base.ILinkListener; |
| import org.eclipse.intent.mapping.base.ILocation; |
| import org.eclipse.intent.mapping.base.ILocationContainer; |
| import org.eclipse.intent.mapping.base.ILocationListener; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * Abstract {@link ILocation} {@link ITreeContentProvider}. |
| * |
| * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a> |
| */ |
| public abstract class AbstractLocationContentProvider implements ITreeContentProvider { |
| |
| /** |
| * The {@link ILinkListener}. |
| * |
| * @author <a href="mailto:yvan.lussaud@obeo.fr">Yvan Lussaud</a> |
| */ |
| private final class LinkListener extends ILinkListener.Stub { |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.intent.mapping.base.ILinkListener.Stub#sourceChanged(org.eclipse.intent.mapping.base.ILocation, |
| * org.eclipse.intent.mapping.base.ILocation) |
| */ |
| public void sourceChanged(ILocation oldSource, ILocation newSource) { |
| update(); |
| refresh(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.intent.mapping.base.ILinkListener.Stub#targetChanged(org.eclipse.intent.mapping.base.ILocation, |
| * org.eclipse.intent.mapping.base.ILocation) |
| */ |
| public void targetChanged(ILocation oldTarget, ILocation newTarget) { |
| update(); |
| refresh(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.intent.mapping.base.ILinkListener.Stub#reportAdded(org.eclipse.intent.mapping.base.IReport) |
| */ |
| public void reportAdded(org.eclipse.intent.mapping.base.IReport report) { |
| if (showReport) { |
| update(); |
| refresh(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.intent.mapping.base.ILinkListener.Stub#reportRemoved(org.eclipse.intent.mapping.base.IReport) |
| */ |
| public void reportRemoved(org.eclipse.intent.mapping.base.IReport report) { |
| if (showReport) { |
| update(); |
| refresh(); |
| } |
| } |
| |
| } |
| |
| /** |
| * Empty {@link Object} array. |
| */ |
| private static final Object[] EMPTY = new Object[0]; |
| |
| /** |
| * The {@link List} of root {@link ILocation}. |
| */ |
| protected final List<ILocation> roots = new ArrayList<ILocation>(); |
| |
| /** |
| * the tree of {@link ILocation}. |
| */ |
| protected final Map<ILocation, List<ILocation>> locationForest = new HashMap<ILocation, List<ILocation>>(); |
| |
| /** |
| * The current {@link Viewer}. |
| */ |
| protected Viewer currentViewer; |
| |
| /** |
| * The current input. |
| */ |
| protected Object currentInput; |
| |
| /** |
| * The {@link ILinkListener}. |
| */ |
| protected final ILinkListener linkListener = new LinkListener(); |
| |
| /** |
| * The {@link Set} of listened {@link ILink}. |
| */ |
| protected Set<ILink> listenedLinks = new HashSet<ILink>(); |
| |
| /** |
| * Should we show {@link org.eclipse.intent.mapping.base.IReport IReport}. Default to <code>false</code>. |
| */ |
| protected boolean showReport; |
| |
| /** |
| * The {@link ILocationListener}. |
| */ |
| protected ILocationListener locationListener = new ILocationListener.Stub() { |
| |
| @Override |
| public void containerChanged(ILocationContainer oldContainer, ILocationContainer newContainer) { |
| update(); |
| refresh(); |
| } |
| |
| @Override |
| public void markedAsDeletedChanged(boolean newValue) { |
| update(); |
| refresh(); |
| } |
| |
| public void changed(String reportDescription) { |
| update(); |
| refresh(); |
| } |
| |
| }; |
| |
| /** |
| * The {@link Set} of listened {@link ILocation}. |
| */ |
| protected Set<ILocation> listenedLocations = new HashSet<ILocation>(); |
| |
| /** |
| * Refresh the {@link Viewer}. |
| */ |
| private void refresh() { |
| if (!currentViewer.getControl().isDisposed()) { |
| if (Display.getDefault().getThread() == Thread.currentThread()) { |
| currentViewer.refresh(); |
| } else { |
| Display.getDefault().asyncExec(new Runnable() { |
| |
| public void run() { |
| currentViewer.refresh(); |
| } |
| }); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.IContentProvider#dispose() |
| */ |
| public void dispose() { |
| clearLocations(); |
| clearLinks(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, |
| * java.lang.Object, java.lang.Object) |
| */ |
| public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { |
| update(viewer, newInput); |
| } |
| |
| /** |
| * Update the content with the given {@link Viewer} and {@link Object input}. |
| * |
| * @param viewer |
| * the {@link Viewer} |
| * @param input |
| * the {@link Object input} |
| */ |
| private void update(Viewer viewer, Object input) { |
| this.currentViewer = viewer; |
| this.currentInput = input; |
| update(); |
| } |
| |
| /** |
| * Update the content. |
| */ |
| protected void update() { |
| clearLocations(); |
| clearLinks(); |
| |
| final List<ILocation> locationLeaves = new ArrayList<ILocation>(); |
| final List<ILink> links = new ArrayList<ILink>(); |
| |
| setLeavesAndLinks(currentInput, locationLeaves, links); |
| |
| installLinkListeners(links); |
| |
| roots.addAll(setLocationForest(currentViewer, locationLeaves)); |
| } |
| |
| /** |
| * Sets {@link ILocation} leaves and {@link ILink}. |
| * |
| * @param input |
| * the current input |
| * @param locationLeaves |
| * the {@link List} of {@link ILocation} leaves to set |
| * @param links |
| * the {@link List} of {@link ILink} to set |
| */ |
| protected abstract void setLeavesAndLinks(Object input, List<ILocation> locationLeaves, |
| List<ILink> links); |
| |
| /** |
| * Installs {@link ILinkListener} on the given {@link ILink}. |
| * |
| * @param links |
| * the {@link List} of {@link ILink} |
| */ |
| private void installLinkListeners(final List<ILink> links) { |
| for (ILink link : links) { |
| listenedLinks.add(link); |
| link.addListener(linkListener); |
| } |
| } |
| |
| /** |
| * Clears the {@link ILocation} and removes {@link ILocationListener}. |
| */ |
| private void clearLocations() { |
| Set<ILocation> currentLocations = new HashSet<ILocation>(roots); |
| |
| while (!currentLocations.isEmpty()) { |
| Set<ILocation> nextLocations = new HashSet<ILocation>(); |
| for (ILocation current : currentLocations) { |
| listenedLocations.remove(current); |
| current.removeListener(locationListener); |
| nextLocations.addAll(locationForest.get(current)); |
| } |
| currentLocations = nextLocations; |
| } |
| |
| roots.clear(); |
| locationForest.clear(); |
| } |
| |
| /** |
| * Clears the {@link ILinkListener}. |
| */ |
| private void clearLinks() { |
| for (ILink link : listenedLinks) { |
| link.removeListener(linkListener); |
| } |
| listenedLinks.clear(); |
| } |
| |
| /** |
| * Sets the {@link ILocation} forest. |
| * |
| * @param viewer |
| * the {@link Viewer}. |
| * @param locationLeaves |
| * the {@link List} of {@link ILocation} leaves. |
| * @return the {@link List} of {@link ILocation} roots. |
| */ |
| private List<ILocation> setLocationForest(Viewer viewer, List<ILocation> locationLeaves) { |
| final List<ILocation> res = new ArrayList<ILocation>(); |
| |
| Set<ILocation> currentLocations = new LinkedHashSet<ILocation>(locationLeaves); |
| for (ILocation leaf : locationLeaves) { |
| locationForest.put(leaf, new ArrayList<ILocation>()); |
| } |
| while (!currentLocations.isEmpty()) { |
| final Set<ILocation> nextLocations = new LinkedHashSet<ILocation>(); |
| for (ILocation current : currentLocations) { |
| if (current != null) { |
| if (!listenedLocations.contains(current)) { |
| listenedLocations.add(current); |
| current.addListener(locationListener); |
| } |
| if (current.getContainer() instanceof ILocation) { |
| final ILocation container = (ILocation)current.getContainer(); |
| List<ILocation> children = locationForest.get(container); |
| if (children == null) { |
| children = new ArrayList<ILocation>(); |
| locationForest.put(container, children); |
| } |
| children.add(current); |
| nextLocations.add(container); |
| } else { |
| res.add(current); |
| } |
| } |
| |
| } |
| currentLocations = nextLocations; |
| } |
| |
| return res; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#getElements(java.lang.Object) |
| */ |
| public Object[] getElements(Object inputElement) { |
| final List<Object> res = new ArrayList<Object>(roots.size()); |
| |
| for (ILocation location : roots) { |
| if (inputElement == location) { |
| final Object[] children = getChildren(inputElement); |
| for (int i = 0; i < children.length; i++) { |
| res.add(i, children[i]); |
| } |
| } else { |
| res.add(location); |
| } |
| } |
| |
| return res.toArray(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) |
| */ |
| public Object[] getChildren(Object parentElement) { |
| final Object[] res; |
| |
| final List<ILocation> children = locationForest.get(parentElement); |
| if (children != null) { |
| res = children.toArray(); |
| } else { |
| res = EMPTY; |
| } |
| |
| return res; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) |
| */ |
| public Object getParent(Object element) { |
| final ILocation res; |
| |
| final ILocationContainer container = ((ILocation)element).getContainer(); |
| if (container instanceof ILocation) { |
| res = (ILocation)container; |
| } else { |
| res = null; |
| } |
| |
| return res; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) |
| */ |
| public boolean hasChildren(Object element) { |
| return !locationForest.get(element).isEmpty(); |
| } |
| |
| } |