org.eclipse.acceleo.internal.ide.ui.views.result.AcceleoResultView.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.acceleo.internal.ide.ui.views.result.AcceleoResultView.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2012 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
 *******************************************************************************/
package org.eclipse.acceleo.internal.ide.ui.views.result;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.acceleo.engine.service.AcceleoService;
import org.eclipse.acceleo.ide.ui.AcceleoUIActivator;
import org.eclipse.acceleo.internal.ide.ui.AcceleoUIMessages;
import org.eclipse.acceleo.internal.parser.cst.utils.FileContent;
import org.eclipse.acceleo.model.mtl.Module;
import org.eclipse.acceleo.model.mtl.ModuleElement;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
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.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.MarkerUtilities;
import org.eclipse.ui.views.navigator.ResourceNavigator;

/**
 * This view shows the files obtained from the last code generation. The code generation creates a model on
 * the fly with the traceability information. The view is connected to the model using a content provider. The
 * view uses a label provider to define how model objects should be presented in the view.
 * 
 * @author <a href="mailto:jonathan.musset@obeo.fr">Jonathan Musset</a>
 */
public class AcceleoResultView extends ResourceNavigator {

    /**
     * The identifier of the result active region marker. It is often the text range synchronized with an
     * EObject or a template element.
     */
    private static final String RESULT_ACTIVE_REGION_MARKER_ID = "org.eclipse.acceleo.ide.ui.activeRegion"; //$NON-NLS-1$

    /**
     * The result view content.
     */
    private AcceleoResultContent content;

    /**
     * The resource change listener to detect that the view must be refreshed.
     */
    private IResourceChangeListener resourceChangeListener;

    /**
     * The selection listener to synchronize the selection in the view.
     */
    private ISelectionListener selectionListener;

    /**
     * The background job to refresh the view.
     */
    private UIJob refreshUIJob;

    /**
     * The resource filter.
     */
    private AcceleoResultFilesFilter previewFilesFilter = new AcceleoResultFilesFilter(this);

    /**
     * A content provider which combines the generated workspace resources and the EMF objects positions in
     * the text.
     * 
     * @author <a href="mailto:jonathan.musset@obeo.fr">Jonathan Musset</a>
     */
    private class AcceleoPreviewContentProvider extends WorkbenchContentProvider {

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.ui.model.BaseWorkbenchContentProvider#getElements(java.lang.Object)
         */
        @Override
        public Object[] getElements(Object element) {
            if (element instanceof String) {
                return new Object[] { (String) element };
            }
            return super.getElements(element);
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.ui.model.BaseWorkbenchContentProvider#getChildren(java.lang.Object)
         */
        @Override
        public Object[] getChildren(Object element) {
            Object[] result;
            if (element instanceof IFile) {
                IPath path = ((IFile) element).getFullPath();
                List<TraceabilityContainer> children = new ArrayList<TraceabilityContainer>();
                if (content != null) {
                    for (TraceabilityTargetFile targetFile : content.getTargetFiles()) {
                        if (path.isPrefixOf(targetFile.getTargetFileFullPath())) {
                            children.addAll(targetFile.getChildren());
                        }
                    }
                }
                result = children.toArray();
            } else if (element instanceof TraceabilityModel) {
                List<Object> children = new ArrayList<Object>();
                children.addAll(((TraceabilityModel) element).getChildren());
                children.addAll(((TraceabilityModel) element).getRegions());
                result = children.toArray();
            } else if (element instanceof TraceabilityRegion) {
                result = new Object[] {};
            } else {
                result = super.getChildren(element);
            }
            return result;
        }

    }

    /**
     * A label provider which combines workspace resources and EMF objects.
     * 
     * @author <a href="mailto:jonathan.musset@obeo.fr">Jonathan Musset</a>
     */
    private class AcceleoPreviewLabelProvider extends DecoratingLabelProvider {

        /**
         * The workspace resources label provider.
         */
        private WorkbenchLabelProvider resourcesLabelProvider;

        /**
         * Constructor.
         */
        public AcceleoPreviewLabelProvider() {
            super(new AdapterFactoryLabelProvider(createAdapterFactory()),
                    getPlugin().getWorkbench().getDecoratorManager().getLabelDecorator());
            resourcesLabelProvider = new WorkbenchLabelProvider();
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.jface.viewers.DecoratingLabelProvider#dispose()
         */
        @Override
        public void dispose() {
            super.dispose();
            resourcesLabelProvider.dispose();
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.jface.viewers.DecoratingLabelProvider#getText(java.lang.Object)
         */
        @Override
        public String getText(Object element) {
            String text = resourcesLabelProvider.getText(element);
            if (text != null && text.length() > 0) {
                return text;
            }

            String result;
            if (element instanceof String) {
                result = (String) element;
            } else if (element instanceof TraceabilityTemplate) {
                result = ((TraceabilityTemplate) element).getLabel();
            } else if (element instanceof TraceabilityModel) {
                result = ((TraceabilityModel) element).getLabel();
            } else if (element instanceof TraceabilityRegion) {
                result = ((TraceabilityRegion) element).getLabel();
            } else {
                result = super.getText(element);
            }
            return result;
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.eclipse.jface.viewers.DecoratingLabelProvider#getImage(java.lang.Object)
         */
        @Override
        public Image getImage(Object element) {
            Image image = resourcesLabelProvider.getImage(element);
            if (image == null) {
                if (element instanceof String) {
                    image = AcceleoUIActivator.getDefault().getImage("icons/AcceleoPreview.gif"); //$NON-NLS-1$
                } else if (element instanceof TraceabilityTemplate) {
                    image = AcceleoUIActivator.getDefault().getImage("icons/template-editor/" //$NON-NLS-1$
                            + ((TraceabilityTemplate) element).getEObject().eClass().getName() + ".gif"); //$NON-NLS-1$
                    if (image == null) {
                        image = getImage(((TraceabilityTemplate) element).getTemplateElement());
                    }
                } else if (element instanceof TraceabilityModel) {
                    image = getImage(((TraceabilityModel) element).getEObject());
                } else if (element instanceof TraceabilityRegion) {
                    image = AcceleoUIActivator.getDefault().getImage("icons/AcceleoRegion.gif"); //$NON-NLS-1$
                } else {
                    image = super.getImage(element);
                }
            }
            return image;
        }
    }

    public AcceleoResultContent getContent() {
        return content;
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ui.views.navigator.ResourceNavigator#initContentProvider(org.eclipse.jface.viewers.TreeViewer)
     */
    @Override
    protected void initContentProvider(TreeViewer viewer) {
        viewer.setContentProvider(new AcceleoPreviewContentProvider());
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ui.views.navigator.ResourceNavigator#initLabelProvider(org.eclipse.jface.viewers.TreeViewer)
     */
    @Override
    protected void initLabelProvider(TreeViewer viewer) {
        viewer.setLabelProvider(new AcceleoPreviewLabelProvider());
    }

    /**
     * Returns a factory built with all the {@link AdapterFactory} instances available in the global registry.
     * 
     * @return a factory built with all the {@link AdapterFactory} instances available in the global registry.
     */
    private AdapterFactory createAdapterFactory() {
        List<AdapterFactory> factories = new ArrayList<AdapterFactory>();
        factories.add(new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE));
        factories.add(new ResourceItemProviderAdapterFactory());
        factories.add(new ReflectiveItemProviderAdapterFactory());
        return new ComposedAdapterFactory(factories);
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ui.views.navigator.ResourceNavigator#initFilters(org.eclipse.jface.viewers.TreeViewer)
     */
    @Override
    protected void initFilters(TreeViewer viewer) {
        viewer.addFilter(previewFilesFilter);
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ui.views.navigator.ResourceNavigator#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createPartControl(Composite parent) {
        super.createPartControl(parent);
        if (content != null) {
            AcceleoService.removeStaticListener(content);
        }
        content = new AcceleoResultContent((ILabelProvider) getTreeViewer().getLabelProvider());
        AcceleoService.addStaticListener(content);
        if (resourceChangeListener == null) {
            resourceChangeListener = new IResourceChangeListener() {
                public void resourceChanged(IResourceChangeEvent event) {
                    refresh();
                }
            };
            ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceChangeListener);
        }
        if (getSite().getPage() != null && selectionListener == null) {
            selectionListener = new ISelectionListener() {
                public void selectionChanged(IWorkbenchPart part, ISelection selection) {
                    if (getContent() != null && selection instanceof TextSelection
                            && ((TextSelection) selection).getOffset() >= 0 && getSite().getPage() != null) {
                        IEditorPart editor = getSite().getPage().getActiveEditor();
                        IFile file;
                        if (editor != null && editor.getEditorInput() != null) {
                            file = (IFile) editor.getEditorInput().getAdapter(IFile.class);
                        } else {
                            file = null;
                        }
                        // Best user experience : We propagate the current selection in the tree viewer only
                        // if the text length is strictly greater than 0
                        if (file != null && ((TextSelection) selection).getLength() > 0) {
                            int offset = ((TextSelection) selection).getOffset()
                                    + ((TextSelection) selection).getLength() - 1;
                            expandElementsAt(file, offset);
                        }
                    }
                }
            };
            getSite().getPage().addPostSelectionListener(selectionListener);
        }
    }

    /**
     * The background job is scheduled to refresh the view.
     */
    private void refresh() {
        if (refreshUIJob != null) {
            refreshUIJob.cancel();
        }
        refreshUIJob = new UIJob("Acceleo") { //$NON-NLS-1$
            @Override
            public IStatus runInUIThread(IProgressMonitor monitor) {
                // if eclipse is closing the tree can be disposed
                final TreeViewer treeViewer = getViewer();
                final Status status = new Status(IStatus.OK, AcceleoUIActivator.PLUGIN_ID, "OK"); //$NON-NLS-1$
                if (treeViewer == null || treeViewer.getTree() == null || treeViewer.getTree().isDisposed()) {
                    return status;
                }

                treeViewer.refresh();
                return status;
            }
        };
        refreshUIJob.setPriority(Job.DECORATE);
        refreshUIJob.setSystem(true);
        refreshUIJob.setProgressGroup(new NullProgressMonitor(), 1);
        final int schedule = 2000;
        refreshUIJob.schedule(schedule);
        refreshUIJob = null;
    }

    /**
     * Expands the traceability element at the given offset, in the traceability information of the given
     * file. It expands the nearest template element and all its ancestors in the view.
     * <p>
     * Here is an example of expanded elements, in order :
     * </p>
     * <p>
     * {IWorspaceRoot, IContainer, IFile, TraceabilityTargetFile, TraceabilityModel, ...,
     * TraceabilityTemplate}
     * </p>
     * 
     * @param generatedFile
     *            is the generated file where to search the offset
     * @param offset
     *            to search in the sub regions
     */
    private void expandElementsAt(IFile generatedFile, int offset) {
        List<Object> elements = getElementsAt(getContent().getTargetFile(generatedFile.getLocation().toString()),
                offset);
        if (elements.size() > 0) {
            Object last = elements.get(elements.size() - 1);
            TreeViewer treeViewer = getTreeViewer();
            treeViewer.setExpandedElements(elements.toArray());
            treeViewer.setSelection(new StructuredSelection(elements), true);
            treeViewer.setSelection(new StructuredSelection(last), true);
            Widget item = treeViewer.testFindItem(last);
            if (item instanceof TreeItem) {
                ((TreeItem) item).setExpanded(false);
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ui.views.navigator.ResourceNavigator#dispose()
     */
    @Override
    public void dispose() {
        super.dispose();
        if (content != null) {
            AcceleoService.removeStaticListener(content);
            content = null;
        }
        if (resourceChangeListener != null) {
            ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceChangeListener);
            resourceChangeListener = null;
        }
        if (selectionListener != null && getSite().getPage() != null) {
            getSite().getPage().removePostSelectionListener(selectionListener);
            selectionListener = null;
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see org.eclipse.ui.views.navigator.ResourceNavigator#handleDoubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
     */
    @Override
    protected void handleDoubleClick(DoubleClickEvent event) {
        if (event.getSelection() instanceof TreeSelection && ((TreeSelection) event.getSelection())
                .getFirstElement() instanceof AbstractTraceabilityElement) {
            AbstractTraceabilityElement element = (AbstractTraceabilityElement) ((TreeSelection) event
                    .getSelection()).getFirstElement();
            AbstractTraceabilityElement fileElement = element;
            while (fileElement != null && !(fileElement instanceof TraceabilityTargetFile)) {
                fileElement = fileElement.getParent();
            }
            IEditorPart part = null;
            if (fileElement instanceof TraceabilityTargetFile) {
                IFile file = ((TraceabilityTargetFile) fileElement).getTargetFile();
                if (file != null && file.isAccessible()) {
                    try {
                        file.deleteMarkers(RESULT_ACTIVE_REGION_MARKER_ID, true, 1);
                        reportActiveRegions(file, FileContent.getFileContent(file.getLocation().toFile()), element);
                        part = IDE.openEditor(getSite().getPage(), file);
                    } catch (PartInitException e) {
                        AcceleoUIActivator.getDefault().getLog().log(e.getStatus());
                    } catch (CoreException e) {
                        AcceleoUIActivator.getDefault().getLog().log(e.getStatus());
                    }
                }
            }
            if (part instanceof ITextEditor) {
                ITextEditor editor = (ITextEditor) part;
                if (element instanceof TraceabilityRegion) {
                    TraceabilityRegion region = (TraceabilityRegion) element;
                    editor.setHighlightRange(region.getTargetFileOffset(), region.getTargetFileLength(), true);
                } else if (element instanceof TraceabilityTemplate) {
                    int b = getMin((TraceabilityTemplate) element);
                    int e = getMax((TraceabilityTemplate) element);
                    editor.setHighlightRange(b, e - b, true);
                } else if (element instanceof TraceabilityModel) {
                    int b = getMin((TraceabilityModel) element);
                    int e = getMax((TraceabilityModel) element);
                    editor.setHighlightRange(b, e - b, true);
                }
            }
        } else {
            super.handleDoubleClick(event);
        }
    }

    /**
     * Adds all the active region markers on the generated file. It means all the regions that have been
     * synchronized with the given traceability element.
     * 
     * @param file
     *            is the file (it is supposed to be a generated file)
     * @param buffer
     *            is the content of the file, it is used to compute the line number of the offset
     * @param element
     *            is the traceability element
     * @throws CoreException
     *             when an issue occurs when creating the markers
     */
    private void reportActiveRegions(IFile file, StringBuffer buffer, AbstractTraceabilityElement element)
            throws CoreException {
        if (element instanceof TraceabilityModel) {
            TraceabilityModel model = (TraceabilityModel) element;
            if (model instanceof TraceabilityTemplate || !hasDirectlyActiveRegion(model)) {
                for (TraceabilityRegion region : model.getRegions()) {
                    reportActiveRegions(file, buffer, region);
                }
                for (TraceabilityModel child : model.getChildren()) {
                    reportActiveRegions(file, buffer, child);
                }
            } else {
                for (TraceabilityRegion region : model.getRegions()) {
                    reportActiveRegions(file, buffer, region);
                }
                for (TraceabilityModel child : model.getChildren()) {
                    if (child instanceof TraceabilityTemplate) {
                        reportActiveRegions(file, buffer, child);
                    }
                }
            }
        } else if (element instanceof TraceabilityContainer) {
            for (TraceabilityModel child : ((TraceabilityContainer) element).getChildren()) {
                reportActiveRegions(file, buffer, child);
            }
        } else if (element instanceof TraceabilityRegion) {
            TraceabilityRegion region = (TraceabilityRegion) element;
            reportActiveRegion(file, buffer, region);
        }
    }

    /**
     * Indicates if the given traceability model element is directly synchronized with a region of the
     * generated text. It is true when it contains a region or a template element in its children (at the
     * first level only).
     * 
     * @param model
     *            is the element to test
     * @return true if the given traceability model element is directly synchronized with a text region
     */
    private boolean hasDirectlyActiveRegion(TraceabilityModel model) {
        boolean hasActiveRegion = model.getRegions().size() > 0;
        if (!hasActiveRegion) {
            for (TraceabilityModel child : model.getChildren()) {
                if (child instanceof TraceabilityTemplate) {
                    hasActiveRegion = true;
                    break;
                }
            }
        }
        return hasActiveRegion;
    }

    /**
     * Adds an active region marker on the generated file.
     * 
     * @param file
     *            is the file (it is supposed to be a generated file)
     * @param buffer
     *            is the content of the file, it is used to compute the line number of the offset
     * @param region
     *            is the traceability region with a beginning offset and an ending offset
     * @throws CoreException
     *             when an issue occurs when creating a marker
     */
    private void reportActiveRegion(IFile file, StringBuffer buffer, TraceabilityRegion region)
            throws CoreException {
        Map<String, Object> map = new HashMap<String, Object>();
        String objectToString = ""; //$NON-NLS-1$
        String featureToString = ""; //$NON-NLS-1$
        String templateToString = ""; //$NON-NLS-1$
        AbstractTraceabilityElement current = region.getParent();
        while (current != null) {
            if (featureToString.length() == 0 && current instanceof TraceabilityTemplate
                    && ((TraceabilityTemplate) current).getTemplateElement() instanceof ModuleElement) {
                featureToString = current.toString();
            } else if (templateToString.length() == 0 && current instanceof TraceabilityTemplate
                    && ((TraceabilityTemplate) current).getTemplateElement() instanceof Module) {
                templateToString = current.toString();
            } else if (objectToString.length() == 0 && current instanceof TraceabilityModel
                    && !(current instanceof TraceabilityTemplate)) {
                objectToString = current.toString();
            }
            current = current.getParent();
        }
        map.put(IMarker.MESSAGE, AcceleoUIMessages.getString("AcceleoResultView.ActiveRegionMarkerMessage", //$NON-NLS-1$
                new Object[] { objectToString, featureToString, templateToString, }));
        map.put(IMarker.SEVERITY, Integer.valueOf(IMarker.SEVERITY_INFO));
        map.put(IMarker.PRIORITY, Integer.valueOf(IMarker.PRIORITY_NORMAL));
        int begin = region.getTargetFileOffset();
        int end = begin + region.getTargetFileLength();
        int line = FileContent.lineNumber(buffer, begin);
        map.put(IMarker.CHAR_START, Integer.valueOf(begin));
        map.put(IMarker.CHAR_END, Integer.valueOf(end));
        map.put(IMarker.LINE_NUMBER, Integer.valueOf(line));
        MarkerUtilities.createMarker(file, map, RESULT_ACTIVE_REGION_MARKER_ID);
    }

    /**
     * Gets the minimum position of the given element in the text.
     * 
     * @param model
     *            is the element to search
     * @return the lower index
     */
    private int getMin(TraceabilityModel model) {
        int min = 0;
        if (model instanceof TraceabilityTemplate || !hasDirectlyActiveRegion(model)) {
            for (TraceabilityRegion region : model.getRegions()) {
                int b = region.getTargetFileOffset();
                if (b > -1 && (b < min || min == 0)) {
                    min = b;
                }
            }
            for (TraceabilityModel child : model.getChildren()) {
                int b = getMin(child);
                if (b > -1 && (b < min || min == 0)) {
                    min = b;
                }
            }
        } else {
            for (TraceabilityRegion region : model.getRegions()) {
                int b = region.getTargetFileOffset();
                if (b > -1 && (b < min || min == 0)) {
                    min = b;
                }
            }
            for (TraceabilityModel child : model.getChildren()) {
                if (child instanceof TraceabilityTemplate) {
                    int b = getMin(child);
                    if (b > -1 && (b < min || min == 0)) {
                        min = b;
                    }
                }
            }
        }
        return min;
    }

    /**
     * Gets the maximum position of the given element in the text.
     * 
     * @param model
     *            is the element to search
     * @return the upper index
     */
    private int getMax(TraceabilityModel model) {
        int max = 0;
        if (model instanceof TraceabilityTemplate || !hasDirectlyActiveRegion(model)) {
            for (TraceabilityRegion region : model.getRegions()) {
                int b = region.getTargetFileOffset();
                int e = b + region.getTargetFileLength();
                if (e > max) {
                    max = e;
                }
            }
            for (TraceabilityModel child : model.getChildren()) {
                int e = getMax(child);
                if (e > max) {
                    max = e;
                }
            }
        } else {
            for (TraceabilityRegion region : model.getRegions()) {
                int b = region.getTargetFileOffset();
                int e = b + region.getTargetFileLength();
                if (e > max) {
                    max = e;
                }
            }
            for (TraceabilityModel child : model.getChildren()) {
                if (child instanceof TraceabilityTemplate) {
                    int e = getMax(child);
                    if (e > max) {
                        max = e;
                    }
                }
            }
        }
        return max;
    }

    /**
     * Gets the traceability element at the given offset, in the descendants of the given traceability
     * container. It returns the nearest template element and all its ancestors in the view.
     * <p>
     * Here is an example of result :
     * </p>
     * <p>
     * {IWorspaceRoot, IContainer, IFile, TraceabilityTargetFile, TraceabilityModel, ...,
     * TraceabilityTemplate}
     * </p>
     * 
     * @param current
     *            is the root element where to search the offset
     * @param offset
     *            to search in the sub regions
     * @return the nearest template element and all its ancestors in the view
     */
    private List<Object> getElementsAt(TraceabilityContainer current, int offset) {
        List<Object> result = new ArrayList<Object>();
        if (current != null) {
            if (current instanceof TraceabilityModel) {
                for (TraceabilityRegion region : ((TraceabilityModel) current).getRegions()) {
                    int b = region.getTargetFileOffset();
                    int e = b + region.getTargetFileLength();
                    if (offset >= b && offset < e) {
                        result.add(current);
                        computeViewAncestors(current, result);
                        break;
                    }
                }
            }
            if (result.size() == 0) {
                for (TraceabilityModel child : current.getChildren()) {
                    result = getElementsAt(child, offset);
                    if (result.size() != 0) {
                        break;
                    }
                }
            }
        }
        return result;
    }

    /**
     * Computes the result view ancestors of the given traceability element.
     * 
     * @param current
     *            is the current element to reveal in the view, the method will compute its ancestors
     * @param result
     *            is the ancestors list, it's an input/output parameter
     */
    private void computeViewAncestors(TraceabilityContainer current, List<Object> result) {
        AbstractTraceabilityElement parent = current.getParent();
        while (parent != null) {
            result.add(0, parent);
            if (parent instanceof TraceabilityTargetFile) {
                break;
            }
            parent = parent.getParent();
        }
        if (parent instanceof TraceabilityTargetFile) {
            IFile targetFile = ((TraceabilityTargetFile) parent).getTargetFile();
            if (targetFile != null) {
                result.add(0, targetFile);
                IContainer container = targetFile.getParent();
                while (container != null) {
                    result.add(0, container);
                    container = container.getParent();
                }
            }
        }
    }

}