Package org.locationtech.udig.document.ui

Source Code of org.locationtech.udig.document.ui.DocumentView$DocumentViewContentProvider

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2011-2012, Refractions Research Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.document.ui;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.miginfocom.swt.MigLayout;
import org.locationtech.udig.catalog.IGeoResource;
import org.locationtech.udig.catalog.document.IAbstractDocumentSource;
import org.locationtech.udig.catalog.document.IAttachment;
import org.locationtech.udig.catalog.document.IAttachmentSource;
import org.locationtech.udig.catalog.document.IDocument;
import org.locationtech.udig.catalog.document.IDocument.ContentType;
import org.locationtech.udig.catalog.document.IDocument.Type;
import org.locationtech.udig.catalog.document.IDocumentFolder;
import org.locationtech.udig.catalog.document.IDocumentSource;
import org.locationtech.udig.catalog.document.IDocumentSource.DocumentInfo;
import org.locationtech.udig.catalog.document.IHotlink;
import org.locationtech.udig.catalog.document.IHotlinkSource;
import org.locationtech.udig.catalog.document.IHotlinkSource.HotlinkDescriptor;
import org.locationtech.udig.core.AdapterUtil;
import org.locationtech.udig.document.source.ShpDocFactory;
import org.locationtech.udig.document.ui.DocumentDialog.Mode;
import org.locationtech.udig.project.IMap;
import org.locationtech.udig.project.internal.commands.edit.SetAttributeCommand;
import org.locationtech.udig.project.ui.ApplicationGIS;
import org.locationtech.udig.tool.info.internal.Messages;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.dialogs.ListDialog;
import org.eclipse.ui.dialogs.PropertyDialogAction;
import org.eclipse.ui.part.ViewPart;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.FidFilterImpl;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.filter.Filter;
import org.opengis.filter.expression.Expression;
import org.opengis.filter.identity.FeatureId;

/**
* The Document View provides a user interface to view, edit, delete and open attached documents. In
* this current release, the view uses shapefile specific implementations of {@link IDocumentSource}
* and {@link IAttachmentSource} as its document sources.
*
* @author paul.pfeiffer
* @author Naz Chan
*/
public class DocumentView extends ViewPart {
   
    private TreeViewer viewer;
    private Button attachButton;
    private Button linkButton;
    private Button editButton;
    private Button openButton;
    private Button saveAsButton;
    private Action saveAsAction;
    private Button removeButton;

    private ResourceSelectionProvider resourceSelectionProvider = new ResourceSelectionProvider();
    private IGeoResource geoResource;
    private SimpleFeature feature;
    private IStructuredSelection viewerSelection;
    private IStructuredSelection workbenchSelection;
    private DocumentItemModel itemModel;
   
    private boolean isActive = false;
    private ISelectionListener workbenchSelectionListener;
    private IPartListener workbenchPartListener;
   
    private boolean isResourceEnabled = false;
    private boolean isFeatureEnabled = false;
    private boolean isHotlinkEnabled = false;
   
    private static final int DOCUMENT_INDEX = 0;
    private static final int DOCUMENT_WEIGHT = 40;
   
    private static final int TYPE_INDEX = 1;
    private static final int TYPE_WEIGHT = 10;
   
    private static final int DESCRIPTION_INDEX = 2;
    private static final int DESCRIPTION_WEIGHT = 50;
   
    public DocumentView() {
        this.itemModel = new DocumentItemModel();
    }

    /**
     * Adds workbench listeners. This will give us control over workbench events that drive the
     * view's contents. Eg. workbench selection, view activation, etc.
     */
    private void addWorkbenchListeners() {

        final IWorkbenchPartSite partSite = getSite();
        final ISelectionService selectionService = partSite.getWorkbenchWindow()
                .getSelectionService();
       
        // Add workbench selection listener
        workbenchSelectionListener = new ISelectionListener() {
            @Override
            public void selectionChanged(IWorkbenchPart part, ISelection selection) {               
                if (!(part instanceof DocumentView)) {
                    handleWorkbenchSelection(selection);   
                }
            }
        };
        selectionService.addPostSelectionListener(workbenchSelectionListener);
       
        // Add workbench part listener
        workbenchPartListener = new IPartListener() {
            @Override
            public void partOpened(IWorkbenchPart part) {
                if (part instanceof DocumentView) {
                    handleWorkbenchSelection(selectionService.getSelection());
                }
            }
            @Override
            public void partDeactivated(IWorkbenchPart part) {
                // Nothing
            }
            @Override
            public void partClosed(IWorkbenchPart part) {
                // Nothing
            }
            @Override
            public void partBroughtToTop(IWorkbenchPart part) {
                // Nothing
            }
            @Override
            public void partActivated(IWorkbenchPart part) {
                isActive = (part instanceof DocumentView);
            }
        };
        partSite.getPage().addPartListener(workbenchPartListener);
       
    }
   
    /**
     * Removes the workbench listeners. This cleans up the listeners we are adding during startup of
     * the view.
     */
    private void removeWorkbenchListeners() {
        if (workbenchSelectionListener != null) {
            getSite().getWorkbenchWindow().getSelectionService()
                    .removePostSelectionListener(workbenchSelectionListener);
            workbenchSelectionListener = null;
        }
        if (workbenchPartListener != null) {
            getSite().getPage().removePartListener(workbenchPartListener);
            workbenchPartListener = null;
        }
    }
   
    @Override
    public void createPartControl(final Composite viewParent) {
       
        setPartName(Messages.docView_name);
        createViewMenu();
       
        final Composite parent = new Composite(viewParent, SWT.NONE);
        final String treeLayoutConst = "insets 0, fill, wrap 2"; //$NON-NLS-1$
        final String treeColConst = "[85%]0[15%]"; //$NON-NLS-1$
        final String treeRowConst = "[100%]"; //$NON-NLS-1$
        parent.setLayout(new MigLayout(treeLayoutConst, treeColConst, treeRowConst));
       
        createTreeControlArea(parent);
        createButtonControlArea(parent);
        refreshBtns();

        addWorkbenchListeners();

    }

    /**
     * Creates the menu items for the view's menu.
     */
    private void createViewMenu() {

        saveAsAction = new Action(Messages.docView_saveAs) {
            @Override
            public void run() {
                saveAs();
            }
        };
        saveAsAction.setId(Messages.docView_saveAs);

        final IActionBars actionBars = getViewSite().getActionBars();
        final IMenuManager menuManager = actionBars.getMenuManager();
        menuManager.add(saveAsAction);
        menuManager.add( new Separator() );
       
        PropertyDialogAction resourcePropertyAction = new PropertyDialogAction( getSite(), resourceSelectionProvider )
        menuManager.add( resourcePropertyAction );
    }
   
    /**
     * Creates the tree-table control for displaying the documents.
     *
     * @param parent
     */
    private void createTreeControlArea(Composite parent) {
       
        final Tree viewerTree = new Tree(parent, SWT.FULL_SELECTION | SWT.BORDER
                | SWT.H_SCROLL | SWT.V_SCROLL);
        viewerTree.setLayoutData("grow, h 100%!, w 85%!"); //$NON-NLS-1$
        viewerTree.setHeaderVisible(true);
        viewerTree.setLinesVisible(true);
       
        final TableLayout viewerTreeLayout = new TableLayout();
       
        final TreeColumn nameColumn = new TreeColumn(viewerTree, SWT.LEFT, DOCUMENT_INDEX);
        nameColumn.setText(Messages.docView_documentColumn);
        viewerTreeLayout.addColumnData(new ColumnWeightData(DOCUMENT_WEIGHT));

        final TreeColumn detailColumn = new TreeColumn(viewerTree, SWT.CENTER, TYPE_INDEX);
        detailColumn.setText(Messages.docView_typeColumn);
        viewerTreeLayout.addColumnData(new ColumnWeightData(TYPE_WEIGHT));
       
        final TreeColumn descColumn = new TreeColumn(viewerTree, SWT.LEFT, DESCRIPTION_INDEX);
        descColumn.setText(Messages.docView_descriptionColumn);
        viewerTreeLayout.addColumnData(new ColumnWeightData(DESCRIPTION_WEIGHT));
       
        viewerTree.setLayout(viewerTreeLayout);
       
        viewer = new TreeViewer(viewerTree);
        viewer.setContentProvider(new DocumentViewContentProvider());
        viewer.setLabelProvider(new DocumentViewTableLabelProvider());
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                handleListSelection(event.getSelection());
            }
        });
        viewer.addDoubleClickListener(new IDoubleClickListener() {
            @Override
            public void doubleClick(DoubleClickEvent event) {
                handleListDoubleClick(event.getSelection());
            }
        });
       
        getSite().setSelectionProvider(viewer);
       
    }
   
    /**
     * Creates the button controls panel for actions related to documents.
     *
     * @param parent
     */
    private void createButtonControlArea(Composite parent) {
       
        final SelectionAdapter btnSelectionListener = new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);
                handleBtnSelection(e.widget);
            }
        };
       
        final Composite btnSection = new Composite(parent, SWT.NONE);
        final String btnLayoutConst = "fillx, wrap 1"; //$NON-NLS-1$
        final String btnColConst = ""; //$NON-NLS-1$
        final String btnRowConst = "[][][][][]push[]"; //$NON-NLS-1$
        btnSection.setLayout(new MigLayout(btnLayoutConst, btnColConst, btnRowConst));
        btnSection.setLayoutData("grow"); //$NON-NLS-1$
       
        final String btnLayoutData = "growx"; //$NON-NLS-1$
       
        // attach button
        attachButton = new Button(btnSection, SWT.PUSH);
        attachButton.setText(Messages.docView_attach);
        attachButton.setLayoutData(btnLayoutData);
        attachButton.addSelectionListener(btnSelectionListener);
       
        // link button
        linkButton = new Button(btnSection, SWT.PUSH);
        linkButton.setText(Messages.docView_link);
        linkButton.setLayoutData(btnLayoutData);
        linkButton.addSelectionListener(btnSelectionListener);
       
        // edit button
        editButton = new Button(btnSection, SWT.PUSH);
        editButton.setText(Messages.docView_edit);
        editButton.setLayoutData(btnLayoutData);
        editButton.addSelectionListener(btnSelectionListener);

        // open button
        openButton = new Button(btnSection, SWT.PUSH);
        openButton.setText(Messages.docView_open);
        openButton.setLayoutData(btnLayoutData);
        openButton.addSelectionListener(btnSelectionListener);
       
        // save as button
        saveAsButton = new Button(btnSection, SWT.PUSH);
        saveAsButton.setText(Messages.docView_saveAs);
        saveAsButton.setLayoutData(btnLayoutData);
        saveAsButton.addSelectionListener(btnSelectionListener);
        saveAsButton.setVisible(false); // Re: Brett's comment
       
        // remove button
        removeButton = new Button(btnSection, SWT.PUSH);
        removeButton.setText(Messages.docView_clear);
        removeButton.setLayoutData(btnLayoutData);
        removeButton.addSelectionListener(btnSelectionListener);
       
    }
   
    @Override
    public void setFocus() {
        // Do something
    }

    @Override
    public void dispose() {
        removeWorkbenchListeners();
        super.dispose();
    }

    /**
     * Handles list selection changes. This should refresh the UI with respect to the current
     * selection.
     *
     * @param selection
     */
    private void handleListSelection(ISelection selection) {
        viewerSelection = null;
        if (selection instanceof StructuredSelection) {
            viewerSelection = (StructuredSelection) selection;
        }
        refreshBtns();
    }
   
    /**
     * Handles list double clicks. This will open the document if the selection is a document and if
     * it is not empty.
     *
     * @param selection
     */
    private void handleListDoubleClick(ISelection selection) {
        final StructuredSelection structSelection = (StructuredSelection) selection;
        final Object element = structSelection.getFirstElement();
        if (element instanceof IDocument) {
            final IDocument doc = (IDocument) element;
            if (!doc.isEmpty()) {
                open();   
            }
        }
    }
   
    /**
     * Refreshes the buttons with respect to the current list selection.
     */
    private void refreshBtns() {
        setBtns(false);
        refreshBtnsOnType();
    }
   
    /**
     * Sets the button's enablement.
     *
     * @param isEnabled
     */
    private void setBtns(boolean isEnabled) {
       
        openButton.setEnabled(isEnabled);
        attachButton.setEnabled(isEnabled);
        linkButton.setEnabled(isEnabled);
        editButton.setEnabled(isEnabled);
        removeButton.setEnabled(isEnabled);
       
        saveAsButton.setEnabled(isEnabled);
        saveAsAction.setEnabled(isEnabled);
    }

    /**
     * Refreshes the buttons with respect to the selection's type (if {@link IDocument} or
     * {@link IDocumentFolder}), document type (refer to {@link Type}) and document content type
     * (refer to {@link ContentType}). And also with respect to the selection's document source. Refer to
     * {@link IAbstractDocumentSource}.
     */
    private void refreshBtnsOnType() {
       
        if (viewerSelection != null) {
            if (viewerSelection.size() == 1) {
               
                final Object element = viewerSelection.getFirstElement();
                final IAbstractDocumentSource folderSource = getDocumentSource();
               
                attachButton.setEnabled(DocSourceUtils.canAttach(folderSource));
                linkButton.setEnabled(DocSourceUtils.canLink(folderSource));
               
                if (element instanceof IDocument) {
                   
                    final IDocument doc = (IDocument) element;
                    final Type docType = doc.getType();
                   
                    editButton.setEnabled(DocSourceUtils.canUpdate(doc.getSource()));
                   
                    if (Type.ATTACHMENT == docType
                            && ContentType.FILE == doc.getContentType()) {
                        saveAsButton.setEnabled(true);
                        saveAsAction.setEnabled(true);
                    }
                   
                    if (!doc.isEmpty()) {
                        openButton.setEnabled(true);
                    }
                   
                    final boolean isHotlink = (Type.HOTLINK == docType);
                    removeButton.setText(isHotlink ? Messages.docView_clear : Messages.docView_delete);
                    removeButton.setEnabled(isHotlink ? !doc.isEmpty() : true);
                    if (removeButton.isEnabled()) {
                        removeButton.setEnabled(DocSourceUtils.canRemove(doc.getSource()));   
                    }
                   
                }
               
            } else if (viewerSelection.size() > 1) {
                int count = 0;
                for (Object obj : viewerSelection.toList()) {
                    if (obj instanceof IDocumentFolder) {
                        count++;
                    }
                }
                final boolean isAllFolders = (count == viewerSelection.size());
                removeButton.setEnabled(!isAllFolders);   
            }
        }
       
    }
   
    /**
     * Handles workbench selection changes. This should refresh the current list with respect to the
     * selection. As the workbench selection change event happens ALOT of times, it is being checked
     * here is the selection is the same as the previous one as to reduce the number of list refreshes.
     *
     * @param selection
     */
    private void handleWorkbenchSelection(ISelection selection) {

        if (selection != null) {
            if (selection instanceof IStructuredSelection) {
                final IStructuredSelection newSelection = (IStructuredSelection) selection;
                if (workbenchSelection == null) {
                    // Go Update!
                    updateList(UpdateType.UPDATE, newSelection);
                } else {
                    final boolean isSameCount = (workbenchSelection.size() == newSelection.size());
                    if (isSameCount) {
                        // Check and update!
                        updateList(UpdateType.CHECK_UPDATE, newSelection);
                    } else {
                        // Go Update!
                        updateList(UpdateType.UPDATE, newSelection);
                    }
                }
                return;
            }
        }
        // Clear list!
        updateList(UpdateType.CLEAR, null);

    }
   
    private enum UpdateType {
        CLEAR, UPDATE, CHECK_UPDATE
    }
   
    /**
     * Creates a job to get the related documents of the selection. This transitions the processing
     * to another thread.
     *
     * @param option
     * @param selection
     */
    private void updateList(final UpdateType option, final IStructuredSelection selection) {

        final Job getDocsJob = new Job(Messages.DocumentView_retrieveDocsProgressMsg){
            @Override
            protected IStatus run(IProgressMonitor monitor) {

                switch (option) {
                case CLEAR:
                    workbenchSelection = null;
                    itemModel = null;
                    break;
                case UPDATE:
                    workbenchSelection = selection;
                    itemModel = new DocumentItemModel();
                    itemModel.setItems(getItems(monitor));

                    break;
                case CHECK_UPDATE:
                    if (!isSameSelection(selection)) {
                        workbenchSelection = selection;
                        itemModel = new DocumentItemModel();
                        itemModel.setItems(getItems(monitor));
                    }
                    break;
                default:
                    break;
                }

                updateListCallback();
                return Status.OK_STATUS;

            }
        };
        getDocsJob.schedule();

    }
   
   
    /**
     * Updates the document list with the related documents of the selection. This transitions the
     * processing back to the UI thread.
     */
    private void updateListCallback() {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                if (viewer != null) {
                    final Tree viewerTree = viewer.getTree();
                    if (viewerTree != null && !viewerTree.isDisposed()) {
                        viewer.setInput(itemModel);
                        viewer.expandAll();
                        if (isActive) {
                            viewerTree.setFocus();
                        }
                    }
                }
            }
        });
    }
   
   
    /**
     * Checks if the selection is the same as the previous one.
     *
     * @param newSelection
     * @return true if same, otherwise false
     */
    private boolean isSameSelection(IStructuredSelection newSelection) {
       
        final Iterator<?> wbIterator = workbenchSelection.iterator();
        final Iterator<?> nwIterator = newSelection.iterator();
        while (nwIterator.hasNext() && wbIterator.hasNext()) {
            final Object wbObj = (Object) wbIterator.next();
            final Object nwObj = (Object) nwIterator.next();
            if (!isSameSelectionObj(wbObj, nwObj)) {
                return false;
            }
        }
       
        return true;
    }
   
   
    /**
     * Checks if the selection object is the same as the previous selection's object in the same
     * index.
     *
     * @param obj1
     * @param obj2
     * @return true if same, otherwise false
     */
    private boolean isSameSelectionObj(Object obj1, Object obj2) {
       
        final IProgressMonitor monitor = new NullProgressMonitor();
       
        final IGeoResource resource1 = toGeoResource(obj1, monitor);
        final IGeoResource resource2 = toGeoResource(obj2, monitor);
        if ((resource1 != null && resource2 != null)
                && (resource1.getID().equals(resource2.getID()))) {

            final FidFilterImpl filter1 = toFilter(obj1, monitor);
            final FidFilterImpl filter2 = toFilter(obj2, monitor);
           
            if (filter1 == null && filter2 == null) {
                return true;
            } else if ((filter1 != null && filter2 != null) && (filter1.equals(filter2))) {
                return true;
            }

        }

        return false;
    }

    void setGeoResourceInternal(IGeoResource geoResource) {
        this.geoResource = geoResource;
        this.resourceSelectionProvider.setSelection( geoResource );
    }

    /**
     * Gets the document items from the current selection.
     *
     * @return document items
     */
    private List<Object> getItems(IProgressMonitor monitor) {
       
        final List<Object> items = new ArrayList<Object>();
        for (Iterator<?> iterator = workbenchSelection.iterator(); iterator.hasNext();) {
           
            final Object obj = iterator.next();
            setGeoResourceInternal( toGeoResource(obj, monitor) );
            if (geoResource != null) {
               
                feature = getFeature(geoResource, toFilter(obj, monitor));
                if (feature != null) {
                   
                    final IHotlinkSource hotlinkSource = toSource(geoResource, IHotlinkSource.class, monitor);
                    isHotlinkEnabled = hotlinkSource != null && hotlinkSource.isEnabled();
                    final IAttachmentSource attachmentSource = toSource(geoResource, IAttachmentSource.class, monitor);
                    isFeatureEnabled = attachmentSource != null && attachmentSource.isEnabled();
                   
                    if (isFeatureEnabled || isHotlinkEnabled) {
                        final String featureLabel = getFeatureLabel(geoResource, feature);
                        final IDocumentFolder folder = ShpDocFactory.createFolder(feature, featureLabel, attachmentSource);
                        if (isFeatureEnabled) {
                            // Set so that source's document list is same with folder's
                            folder.setDocuments(attachmentSource.getDocuments(feature, monitor));   
                        }
                        if (isHotlinkEnabled) {
                            folder.insertDocuments(hotlinkSource.getDocuments(feature, monitor), 0);
                        }
                        items.add(folder);
                    }
                   
                }
               
                final IDocumentSource docSource = toSource(geoResource, IDocumentSource.class, monitor);
                isResourceEnabled = docSource != null && docSource.isEnabled();
               
                if (isResourceEnabled) {
                    final IDocumentFolder folder = ShpDocFactory.createFolder(null, geoResource.getTitle(), docSource);
                    // Set so that source's document list is same with folder's
                    folder.setDocuments(docSource.getDocuments(monitor));
                    items.add(folder);
                }
               
            }
        }
       
        return items;
       
    }
   
    private static final String FEATURE_LABEL = "FEATURE_LABEL"; //$NON-NLS-1$
   
    /**
     * Gets the feature label by running the feature label expression set for the feature.
     *
     * @param resource
     * @param feature
     * @return feature label
     */
    private String getFeatureLabel(IGeoResource resource, SimpleFeature feature) {
       
        final String labelExpression = (String) resource.getPersistentProperties().get(
                FEATURE_LABEL);
       
        if (labelExpression != null) {
            try {
                final Expression exp = ECQL.toExpression(labelExpression);
                final String featureLabel = (String) exp.evaluate(feature);
                if (featureLabel != null && featureLabel.trim().length() > 0) {
                    return featureLabel;   
                }
            } catch (CQLException e) {
                e.printStackTrace();
            }   
        }
       
        return feature.getID();
       
    }
   
    /**
     * Resolves the object to a geo resource. This returns null if it is not able to resolve.
     *
     * @param obj
     * @param monitor
     * @return geo resource
     */
    private IGeoResource toGeoResource(Object obj, IProgressMonitor monitor) {
        if (obj != null) {
            final AdapterUtil adapterUtil = AdapterUtil.instance;
            if (adapterUtil.canAdaptTo(obj, IGeoResource.class)) {
                try {
                    return adapterUtil.adaptTo(IGeoResource.class, obj, monitor);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }      
        }
        return null;
    }

    /**
     * Resolves the object to a document source. This returns null if it is not able to resolve.
     *
     * @param geoResource
     * @param type
     * @param monitor
     * @return document source
     */
    private <T> T toSource(IGeoResource geoResource, Class<T> type, IProgressMonitor monitor) {
        if (geoResource != null) {
            if (geoResource.canResolve(type)) {
                try {
                    return geoResource.resolve(type, monitor);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }   
        }
        return null;
    }

    /**
     * Resolves the object to a feature ID filter. This returns null if it is unable to resolve.
     *
     * @param obj
     * @param monitor
     * @return feature ID filter
     */
    private FidFilterImpl toFilter(Object obj, IProgressMonitor monitor) {
        if (obj != null) {
            final AdapterUtil adapterUtil = AdapterUtil.instance;
            try {
                return adapterUtil.adaptTo(FidFilterImpl.class, obj, monitor);
            } catch (ClassCastException e) {
                // Selection does not include a feature
                // e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }   
        }
        return null;
    }
   
    /**
     * Gets the feature from the geo resource given the filter.
     *
     * @param geoResource
     * @param filter
     * @return feature
     */
    private SimpleFeature getFeature(IGeoResource geoResource, FidFilterImpl filter) {
        if (filter != null) {
            try {
                if (geoResource.canResolve(SimpleFeatureStore.class)) {
                    final SimpleFeatureStore featureSource = geoResource.resolve(SimpleFeatureStore.class,
                            new NullProgressMonitor());
                    final SimpleFeatureCollection featureCollection = featureSource.getFeatures(filter);
                    final SimpleFeatureIterator featureIterator = featureCollection.features();
                    try {
                         if (featureIterator.hasNext()) {
                             return featureIterator.next();
                         }
                    } finally {
                        if (featureIterator != null) {
                            featureIterator.close();
                        }
                    }   
                }
            } catch (IOException e) {
                e.printStackTrace();
            }           
        }
        return null;
    }
   
    /**
     * Handles button selection (click) actions.
     *
     * @param btn
     */
    private void handleBtnSelection(Widget btn) {
        if (openButton == btn) {
            open();
        } else if (attachButton == btn) {
            attach();
        } else if (linkButton == btn) {
            link();
        } else if (editButton == btn) {
            edit();
        } else if (removeButton == btn) {
            remove();
        } else if (saveAsButton == btn) {
            saveAs();
        }
    }
   
    /**
     * Opens the documents in the current selection.
     */
    private void open() {
        final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
        final IDocument doc = (IDocument) selection.getFirstElement();
        if (ContentType.ACTION == doc.getContentType()) {
            openAction(doc);
        } else {
            doc.open()
        }
    }

    /**
     * Opens the action document - action selection dialog that allows the user to select the action
     * to be opened.
     *
     * @param doc
     */
    private void openAction(IDocument doc) {
       
        final IHotlink hotlinkDoc = (IHotlink) doc;
        final List<HotlinkDescriptor> descriptors = hotlinkDoc.getDescriptors();
       
        if (descriptors.size() > 1) {
            final ListDialog dialog = new ListDialog(openButton.getShell());
            dialog.setTitle(Messages.DocumentView_openActionDialogTitle);
            dialog.setMessage(Messages.DocumentView_openActionDialogMessage);
            dialog.setLabelProvider(new LabelProvider() {
                @Override
                public String getText(Object element) {
                    final HotlinkDescriptor descriptor = (HotlinkDescriptor) element;
                    return DocUtils.getLabelAndDescDisplay(descriptor.getLabel(),
                            descriptor.getDescription());
                }
            });
            dialog.setContentProvider(new ArrayContentProvider());
            dialog.setInput(descriptors.toArray());
            dialog.setInitialElementSelections(Collections.singletonList(descriptors.get(0)));
            if (Dialog.OK == dialog.open()) {
                final Object[] results = dialog.getResult();
                if (results != null && results.length > 0) {
                    final HotlinkDescriptor descriptor = (HotlinkDescriptor) results[0];
                    openAction(hotlinkDoc, descriptor);
                }
            }           
        } else if (descriptors.size() == 1) {
            final HotlinkDescriptor descriptor = descriptors.get(0);
            openAction(hotlinkDoc, descriptor);
        }
       
    }
   
    /**
     * Opens the action document. This gets the value from the hotlink document and the action
     * definition from the desriptor.
     *
     * @param hotlinkDoc
     * @param descriptor
     */
    private void openAction(IHotlink hotlinkDoc, HotlinkDescriptor descriptor) {
        final String action = descriptor.getConfig().replace(DocumentPropertyPage.ACTION_PARAM,
                hotlinkDoc.getContent().toString());
        Program.launch(action);
    }
   
    /**
     * Adds a new document. This opens the {@link DocumentDialog} to allow the user to input details
     * of the document to be added.
     */
    private void attach() {

        final IDocumentFolder folder = getDocumentFolder();
        if (folder != null) {
            final Map<String, Object> params = new HashMap<String, Object>();
            params.put(DocumentDialog.P_TYPE, Type.ATTACHMENT);
            params.put(DocumentDialog.P_MODE, Mode.ADD);
            params.put(DocumentDialog.P_TEMPLATES, itemModel.getTemplates(folder));

            final IAbstractDocumentSource source = folder.getSource();
            final boolean isFeatureDoc = (source instanceof IAttachmentSource);
            final DocumentDialog docDialog = openDocDialog(new HashMap<String, Object>(), params, isFeatureDoc);
            if (docDialog != null) {
                addDocument(folder, docDialog.getDocInfo());
            }           
        }
       
    }

    /**
     * Links a new document. This opens the {@link DocumentDialog} to allow the user to input details
     * of the document to be linked.
     */
    private void link() {

        final IDocumentFolder folder = getDocumentFolder();
        if (folder != null) {
            final IAbstractDocumentSource source = folder.getSource();
           
            final Map<String, Object> params = new HashMap<String, Object>();
            params.put(DocumentDialog.P_TYPE, Type.LINKED);
            params.put(DocumentDialog.P_MODE, Mode.ADD);
            params.put(DocumentDialog.P_TEMPLATES, itemModel.getTemplates(folder));
            final List<ContentType> contentTypes = new ArrayList<ContentType>();
            if (DocSourceUtils.canLinkFile(source)) {
                contentTypes.add(ContentType.FILE);
            }
            if (DocSourceUtils.canLinkWeb(source)) {
                contentTypes.add(ContentType.WEB);
            }
            if (contentTypes != null && contentTypes.size() > 0) {
                params.put(DocumentDialog.P_CONTENT_TYPES, contentTypes);   
            }
           
            final boolean isFeatureDoc = (source instanceof IAttachmentSource);
            final DocumentDialog docDialog = openDocDialog(new HashMap<String, Object>(), params, isFeatureDoc);
            if (docDialog != null) {
                addDocument(folder, docDialog.getDocInfo());
            }           
        }
       
    }
   
    /**
     * Adds a new document to the document folder with the given document info.
     */
    private void addDocument(final IDocumentFolder folder, final DocumentInfo info) {
               
        final Job addDocJob = new Job(Messages.DocumentView_addDocProgressMsg){
            @Override
            protected IStatus run(IProgressMonitor monitor) {

                IDocument doc = null;
                if (folder.getSource() instanceof IDocumentSource) {
                    final IDocumentSource resourceDocSource = (IDocumentSource) folder.getSource();
                    doc = resourceDocSource.add(info, monitor);
                } else if (folder.getSource() instanceof IAttachmentSource) {
                    final IAttachmentSource featureDocSource = (IAttachmentSource) folder.getSource();
                    doc = featureDocSource.add(feature, info, monitor);
                }               
               
                addDocumentCallback(doc);
                return Status.OK_STATUS;

            }
        };
        addDocJob.schedule();

    }
   
    /**
     * Refreshes the documents list after adding a document. This transitions the processing back to
     * the UI thread.
     */
    private void addDocumentCallback(final IDocument doc) {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                if (doc == null) {
                    MessageDialog.openError(attachButton.getShell(),
                            Messages.DocumentView_addDocPopupTitle, Messages.DocumentView_addDocError);
                } else {
                    viewer.refresh();
                    viewer.expandAll();   
                }
            }
        });
    }
   
    /**
     * Edits an existing document. This opens the {@link DocumentDialog} to allow the user to input details
     * of the document to be edited.
     */
    private void edit() {
       
        final Object obj = viewerSelection.getFirstElement();
        if (obj instanceof IDocument) {
            final IDocument doc = (IDocument) obj;
            if (Type.HOTLINK == doc.getType()) {
                editHotlink(doc);
            } else {
                editDocument(doc);
            }
        }
       
    }
   
    /**
     * Edits an existing document.
     *
     * @param doc
     */
    private void editDocument(final IDocument doc) {
       
        final Map<String, Object> values = new HashMap<String, Object>();
        if (!doc.isEmpty()) {
            values.put(DocumentDialog.V_INFO, doc.getContent().toString());   
        }
        values.put(DocumentDialog.V_CONTENT_TYPE, doc.getContentType());
        values.put(DocumentDialog.V_LABEL, doc.getLabel());
        values.put(DocumentDialog.V_DESCRIPTION, doc.getDescription());
        values.put(DocumentDialog.V_TEMPLATE, doc.isTemplate());
       
        final Map<String, Object> params = new HashMap<String, Object>();
        params.put(DocumentDialog.P_TYPE, doc.getType());
        params.put(DocumentDialog.P_MODE, Mode.EDIT);
        params.put(DocumentDialog.P_TEMPLATES, itemModel.getTemplates(doc));
       
        final IAbstractDocumentSource source = doc.getSource();
        final boolean isFeatureDoc = (source instanceof IAttachmentSource);
        final DocumentDialog docDialog = openDocDialog(values, params, isFeatureDoc);
        if (docDialog != null) {
            final Job editDocJob = new Job(Messages.DocumentView_updateDocProgressMsg){
                @Override
                protected IStatus run(IProgressMonitor monitor) {
                    boolean isUpdated = false;
                    final DocumentInfo info = docDialog.getDocInfo();
                    if (source instanceof IDocumentSource) {
                        final IDocumentSource resourceDocSource = (IDocumentSource) source;
                        isUpdated = resourceDocSource.update(doc, info, monitor);
                    } else if (source instanceof IAttachmentSource) {
                        final IAttachmentSource featureDocSource = (IAttachmentSource) source;
                        isUpdated = featureDocSource.update(feature, doc, info, monitor);
                    }             
                    editDocumentCallback(isUpdated);
                    return Status.OK_STATUS;
                }
            };
            editDocJob.schedule();
        }
       
    }
   
    /**
     * Refreshes the documents list after updating a document. This transitions the processing back
     * to the UI thread.
     */
    private void editDocumentCallback(final boolean isUpdated) {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                if (!isUpdated) {
                    MessageDialog.openError(attachButton.getShell(),
                            Messages.DocumentView_updateDocPopupTitle, Messages.DocumentView_updateDocError);
                } else {
                    viewer.refresh();
                    viewer.expandAll();   
                }
            }
        });
    }
   
    /**
     * Edits an existing hotlink document.
     *
     * @param doc
     */
    private void editHotlink(final IDocument doc) {
       
        final IHotlink hotlinkDoc = (IHotlink) doc;
        final String attributeName = hotlinkDoc.getAttributeName();
       
        final Map<String, Object> values = new HashMap<String, Object>();
        if (!doc.isEmpty()) {
            values.put(DocumentDialog.V_INFO, doc.getContent().toString());   
        }
        values.put(DocumentDialog.V_CONTENT_TYPE, doc.getContentType());
        values.put(DocumentDialog.V_ATTRIBUTE, attributeName);
        values.put(DocumentDialog.V_LABEL, doc.getLabel());
        values.put(DocumentDialog.V_DESCRIPTION, doc.getDescription());
        if (ContentType.ACTION == doc.getContentType()) {
            values.put(DocumentDialog.V_ACTIONS, hotlinkDoc.getDescriptors());
        }
       
        final Map<String, Object> params = new HashMap<String, Object>();
        params.put(DocumentDialog.P_TYPE, Type.HOTLINK);
        params.put(DocumentDialog.P_MODE, Mode.EDIT);
        if (ContentType.FILE == doc.getContentType()) {
            params.put(DocumentDialog.P_TEMPLATES, itemModel.getTemplates(doc));
        }
       
        final DocumentDialog docDialog = openDocDialog(values, params, true);
        if (docDialog != null) {
            final Job editHotlinkJob = new Job(Messages.DocumentView_updateHotlinkProgressMsg){
                @Override
                protected IStatus run(IProgressMonitor monitor) {
                    boolean isUpdated = false;
                    final IHotlinkSource featureDocSource = (IHotlinkSource) doc.getSource();
                    switch (doc.getContentType()) {
                    case FILE:
                        isUpdated = featureDocSource.setFile(feature, attributeName, docDialog.getFileInfo(), monitor);
                        break;
                    case WEB:
                        isUpdated = featureDocSource.setLink(feature, attributeName, docDialog.getUrlInfo(), monitor);
                        break;
                    case ACTION:
                        isUpdated = featureDocSource.setAction(feature, attributeName, docDialog.getInfo(), monitor);
                        break;
                    default:
                        break;
                    }
                    if (isUpdated) {
                        isUpdated = set(attributeName, feature.getAttribute(attributeName), monitor);
                    }
                    itemModel.getClass();
                    editHotlinkCallback(isUpdated);
                    return Status.OK_STATUS;
                }
            };
            editHotlinkJob.schedule();
        }
       
    }
   
    /**
     * Refreshes the documents list after updating a hotlink. This transitions the processing back
     * to the UI thread.
     */
    private void editHotlinkCallback(final boolean isUpdated) {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                if (!isUpdated) {
                    MessageDialog.openError(attachButton.getShell(),
                            Messages.DocumentView_setHotlinkPopupTitle, Messages.DocumentView_setHotlinkError);
                } else {
                    viewer.refresh();
                    viewer.expandAll();   
                }
            }
        });
    }
   
    /**
     * Opens the {@link DocumentDialog} for inputting document metadata.
     *
     * @param values
     * @param params
     * @return document info
     */
    private DocumentDialog openDocDialog(Map<String, Object> values, Map<String, Object> params,
            boolean isFeatureDoc) {
        if (isFeatureDoc && feature != null) {
            params.put(DocumentDialog.P_FEATURE_NAME, getFeatureLabel(geoResource, feature));
        }
        params.put(DocumentDialog.P_RESOURCE_NAME, geoResource.getTitle());
        final DocumentDialog docDialog = new DocumentDialog(editButton.getShell(), values, params);
        final int result = docDialog.open();
        if (Dialog.OK == result) {
            return docDialog;
        }
        return null;
    }
   
   
    /**
     * Removes the documents in the current selection.
     */
    private void remove() {

        final Map<IAbstractDocumentSource, ArrayList<IDocument>> docMap = new HashMap<IAbstractDocumentSource, ArrayList<IDocument>>();

        final Iterator<?> iterator = viewerSelection.iterator();
        while (iterator.hasNext()) {
            final Object obj = iterator.next();
            if (obj instanceof IDocument) {
                final IDocument doc = (IDocument) obj;
                if (docMap.containsKey(doc.getSource())) {
                    docMap.get(doc.getSource()).add(doc);
                } else {
                    final ArrayList<IDocument> docs = new ArrayList<IDocument>();
                    docs.add(doc);
                    docMap.put(doc.getSource(), docs);
                }
            }
        }

        for (final IAbstractDocumentSource source : docMap.keySet()) {
            final ArrayList<IDocument> docs = docMap.get(source);
            for (final IDocument doc : docs) {
               
                boolean doDelete = true;
                if (Type.ATTACHMENT == doc.getType() && ContentType.FILE == doc.getContentType()
                        && !doc.isEmpty()) {
                    doDelete = MessageDialog.openConfirm(removeButton.getShell(),
                            Messages.docView_deleteAttachConfirmTitle,
                            Messages.docView_deleteAttachConfirmMsg);
                }
               
                if (doDelete) {
                    if (source instanceof IDocumentSource) {
                        final Job removeLayerDocJob = new Job(Messages.DocumentView_removeDocProgressMsg){
                            @Override
                            protected IStatus run(IProgressMonitor monitor) {
                                final IDocumentSource docSource = (IDocumentSource) source;
                                final boolean isRemoved = docSource.remove(doc, monitor);
                                removeDocumentCallback(isRemoved);
                                return Status.OK_STATUS;
                            }
                        };
                        removeLayerDocJob.schedule();
                    } else if (source instanceof IAttachmentSource) {
                        final IAttachmentSource featureDocSource = (IAttachmentSource) source;
                        final Job removeFeatureDocJob = new Job(Messages.DocumentView_removeDocProgressMsg){
                            @Override
                            protected IStatus run(IProgressMonitor monitor) {
                                final boolean isRemoved = featureDocSource.remove(feature, doc, monitor);
                                removeDocumentCallback(isRemoved);
                                return Status.OK_STATUS;
                            }
                        };
                        removeFeatureDocJob.schedule();
                    } else if (source instanceof IHotlinkSource) {
                        final IHotlinkSource featureHotlinkSource = (IHotlinkSource) source;
                        final Job clearHotlinkJob = new Job(Messages.DocumentView_clearHotlinkProgressMsg) {
                            @Override
                            protected IStatus run(IProgressMonitor monitor) {
                                final IHotlink hotlinkDoc = (IHotlink) doc;
                                final String attributeName = hotlinkDoc.getAttributeName();
                                boolean isCleared = featureHotlinkSource.clear(feature,
                                        attributeName, monitor);
                                if (isCleared) {
                                    isCleared = set(attributeName, null, monitor);
                                }
                                clearHotlinkCallback(isCleared);
                                return Status.OK_STATUS;
                            }
                        };
                        clearHotlinkJob.schedule();
                    }                   
                }
               
            }
        }
       
    }
   
    /**
     * Refreshes the documents list after clearing a hotlink. This transitions the processing back
     * to the UI thread.
     */
    private void clearHotlinkCallback(final boolean isCleared) {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                if (!isCleared) {
                    MessageDialog.openError(attachButton.getShell(),
                            Messages.DocumentView_clearHotlinkPopupTitle, Messages.DocumentView_clearHotlinkError);
                } else {
                    viewer.refresh();
                    viewer.expandAll();   
                }
            }
        });
    }
   
    /**
     * Refreshes the documents list after removing a document. This transitions the processing back
     * to the UI thread.
     */
    private void removeDocumentCallback(final boolean isRemoved) {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                if (!isRemoved) {
                    MessageDialog.openError(attachButton.getShell(),
                            Messages.DocumentView_removeDocPopupTitle, Messages.DocumentView_removeDocError);
                } else {
                    viewer.refresh();
                    viewer.expandAll();   
                }
            }
        });
    }
   
    /**
     * Sets the attribute value of the feature.
     *
     * @param fid
     * @param attributeName
     * @param obj
     */
    private boolean set(final String attributeName, final Object obj, IProgressMonitor monitor) {
        final FeatureId fid = feature.getIdentifier();
        final IMap map = ApplicationGIS.getActiveMap();
        if (map != null) {
            return setOnMap(map, fid, attributeName, obj);
        } else {
            return setOnGeoResource(fid, attributeName, obj, monitor);
        }
    }
   
    /**
     * Sets the attribute value of the feature given that the layer in on the map.
     *
     * @param map
     * @param fid
     * @param attributeName
     * @param obj
     */
    private boolean setOnMap(final IMap map, final FeatureId fid, final String attributeName,
            final Object obj) {
        final SetAttributeCommand cmd = new SetAttributeCommand(attributeName, obj);
        map.sendCommandASync(cmd);
        return true;
    }
   
    /**
     * Sets the attribute value of the feature directly to the geoResource.
     *
     * @param fid
     * @param attributeName
     * @param obj
     */
    private boolean setOnGeoResource(final FeatureId fid, final String attributeName,
            final Object obj, IProgressMonitor monitor) {

        try {
            if (geoResource.canResolve(SimpleFeatureStore.class)) {
                final Filter filter = CommonFactoryFinder.getFilterFactory2().id(fid);
                final SimpleFeatureStore featureStore = geoResource.resolve(
                        SimpleFeatureStore.class, monitor);
                featureStore.modifyFeatures(attributeName, obj, filter);
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
       
        return false;

    }
   
    /**
     * Saves the current file document's file as another file.
     */
    private void saveAs() {

        final Object element = viewerSelection.getFirstElement();
        if (element instanceof IAttachment) {
            final IAttachment attachDoc = (IAttachment) element;
            final File file = (File) attachDoc.getContent();
           
            final FileDialog dialog = new FileDialog(saveAsButton.getShell(), SWT.SAVE);
            dialog.setText(Messages.DocumentView_saveAsDialogTitle);
            dialog.setOverwrite(true);
            dialog.setFileName(DocUtils.getSaveAsFilename(file));
            final String filePath = dialog.open();
            if (filePath != null) {
                final File newFile = new File(DocUtils.cleanFilename(filePath, file));
                final boolean isSaved = attachDoc.saveAs(newFile);
                if (isSaved) {
                    // Program.launch(file.getAbsolutePath());
                    MessageDialog.openInformation(saveAsButton.getShell(),
                            Messages.DocumentView_saveAsSuccessDialogTitle,
                            Messages.DocumentView_saveAsSuccessDialogMsg);
                } else {
                    MessageDialog.openError(saveAsButton.getShell(),
                            Messages.DocumentView_saveAsErrorDialogTitle,
                            Messages.DocumentView_saveAsErrorDialogMsg);
                }
            }
        }

    }
   
    // Utility inner classes
   
    /**
     * The label provider for the document item tree viewer in the {@link DocumentView}.
     */
    private class DocumentViewTableLabelProvider implements ITableLabelProvider {

        private DocumentImageProvider imageProvider;
       
        public DocumentViewTableLabelProvider() {
            imageProvider = new DocumentImageProvider();
        }
       
        @Override
        public Image getColumnImage(Object element, int columnIndex) {
            switch (columnIndex) {
            case DOCUMENT_INDEX:
                if (element instanceof IDocumentFolder) {
                    return imageProvider.createFolderImage();
                } else if (element instanceof IDocument) {
                    final IDocument doc = (IDocument) element;               
                    return imageProvider.createDocumentImage(doc);
                }
                return imageProvider.createDefaultImage();
            }
            return null;
        }

        @Override
        public String getColumnText(Object element, int columnIndex) {
            if (element instanceof IDocumentFolder) {
                final IDocumentFolder folder = (IDocumentFolder) element;
                switch (columnIndex) {
                case DOCUMENT_INDEX:
                    return DocUtils.toCamelCase(folder.getName());
                }
            } else if (element instanceof IDocument) {
                final IDocument doc = (IDocument) element;
                switch (columnIndex) {
                case DOCUMENT_INDEX:
                    return DocUtils.getDocStr(doc);
                case TYPE_INDEX:
                    if (doc.isTemplate()) {
                        return Messages.DocumentView_templateLbl;
                    }
                    return DocUtils.toCamelCase(doc.getContentType().toString());
                case DESCRIPTION_INDEX:
                    final String description = doc.getDescription();
                    if (description != null) {
                        return description;   
                    }
                    return ""; //$NON-NLS-1$
                }               
            }
            return null;
        }

        @Override
        public void dispose() {
            // Nothing
        }

        @Override
        public boolean isLabelProperty(Object element, String property) {
            return false;
        }

        @Override
        public void addListener(ILabelProviderListener listener) {
            // Nothing
        }
       
        @Override
        public void removeListener(ILabelProviderListener listener) {
            // Nothing
        }

    }

    /**
     * The content provider for the document item tree viewer in the {@link DocumentView}.
     */
    private class DocumentViewContentProvider implements ITreeContentProvider {

        @Override
        public Object[] getElements(Object inputElement) {
            if (inputElement instanceof DocumentItemModel) {
                final DocumentItemModel itemModel = (DocumentItemModel) inputElement;
                return itemModel.getItems().toArray(new Object[0]);   
            }
            return null;
        }

        @Override
        public Object[] getChildren( Object element ) {
            if (element instanceof IDocumentFolder) {
                final IDocumentFolder folder = (IDocumentFolder) element;
                return folder.getItems().toArray(new Object[0]);
            }
            return null;
        }

        @Override
        public Object getParent( Object element ) {
            return null;
        }

        @Override
        public boolean hasChildren( Object element ) {
            if (element instanceof IDocumentFolder) {
                final IDocumentFolder folder = (IDocumentFolder) element;
                return folder.getItems().size() > 0;
            }
            return false;
        }

        @Override
        public void inputChanged( Viewer viewer, Object oldInput, Object newInput ) {
            // Do input change stuff here
        }
       
        @Override
        public void dispose() {
            // Do dispose stuff here
        }
       
    }
    /**
     * The data model for the document item tree viewer in the {@link DocumentView}.
     */
    private class DocumentItemModel {
       
        private List<Object> items;
       
        public DocumentItemModel() {
            items = new ArrayList<Object>();
        }
       
        public List<Object> getItems() {
            return items;
        }
       
        public void setItems(List<Object> items) {
            this.items = items;
        }
       
        public IDocumentFolder getFolder(IDocument doc) {
            for (Object item : items) {
                if (item instanceof IDocumentFolder) {
                    final IDocumentFolder folder = (IDocumentFolder) item;
                    for (IDocument folderDoc : folder.getDocuments()) {
                        if (folderDoc.equals(doc)) {
                            return folder;
                        }
                    }
                    // if (folder.getSource().equals(doc.getSource())) {
                    // return folder;
                    // }
                }
            }
            return null;
        }
    
        public List<IDocument> getTemplates(IDocumentFolder folder) {
            return getTemplates(folder.getSource(), folder, null);
        }
       
        public List<IDocument> getTemplates(IDocument doc) {
            return getTemplates(doc.getSource(), getFolder(doc), doc);
        }
       
        private List<IDocument> getTemplates(IAbstractDocumentSource source,
                IDocumentFolder folder, IDocument doc) {
            if (source instanceof IDocumentSource) {
                return getTemplatesInternal(folder, doc);
            } else {
                return getTemplatesInternal(doc);
            }
        }
       
        private List<IDocument> getTemplatesInternal(IDocumentFolder folder, IDocument refDoc) {
           
            final List<IDocument> templates = new ArrayList<IDocument>();
            for (IDocument doc : folder.getDocuments()) {
                if (!doc.equals(refDoc) && doc.isTemplate()) {
                    templates.add(doc);
                }
            }
            return templates;
        }
       
        private List<IDocument> getTemplatesInternal(IDocument refDoc) {
            final List<IDocument> templates = new ArrayList<IDocument>();
            for (Object item : items) {
                if (item instanceof IDocumentFolder) {
                    final IDocumentFolder folder = (IDocumentFolder) item;
                    templates.addAll(getTemplatesInternal(folder, refDoc));
                }
            }
            return templates;
        }
       
    }
    /**
     * Gets the document folder containing the current selection. Or the current selection if it is
     * a folder.
     *
     * @return document folder
     */
    private IDocumentFolder getDocumentFolder() {
        if (viewerSelection != null) {
            final Object obj = viewerSelection.getFirstElement();
            IDocumentFolder folder = null;
            if (obj instanceof IDocumentFolder) {
                folder = (IDocumentFolder) obj;
            } else if (obj instanceof IDocument) {
                folder = itemModel.getFolder((IDocument) obj);
            }
            return folder;   
        }
        return null;
    }
   
    /**
     * Gets the document source of the folder containing the current selection. This should also be
     * the same document source of the current selection if it is not a folder.
     *
     * @return document source
     */
    private IAbstractDocumentSource getDocumentSource() {
        final IDocumentFolder folder = getDocumentFolder();
        if (folder != null) {
            return folder.getSource();   
        }
        return null;
    }
   
    /**
     * Selection provider that reports back the {@link #geoReosurce}.
     */
    public class ResourceSelectionProvider implements ISelectionProvider {
        protected ListenerList listeners;
        ISelection selection;
       
        public ResourceSelectionProvider() {
            listeners = new ListenerList( ListenerList.IDENTITY );
            selection = StructuredSelection.EMPTY;
        }

        @Override
        public void addSelectionChangedListener(ISelectionChangedListener listener) {
            listeners.add( listener );
        }
        @Override
        public void removeSelectionChangedListener(ISelectionChangedListener listener) {
            listeners.remove(listener);
        }

        @Override
        public ISelection getSelection() {
            return this.selection;
        }
        public void setSelection(IGeoResource resource){
            if ( resource != null ){
                this.selection = new StructuredSelection( DocumentView.this.geoResource );
            }
            else {
                this.selection = StructuredSelection.EMPTY; // no dice!
            }           
        }
       
        @Override
        public void setSelection(ISelection selection) {
            this.selection = selection;
            fire();
        }
       
        private void fire() {
            SelectionChangedEvent event = null;
            for( Object item : listeners.getListeners() ){
                ISelectionChangedListener listener = (ISelectionChangedListener) item;
                if( event == null ){
                    event = new SelectionChangedEvent( this, this.selection);
                }
                listener.selectionChanged( event );               
            }
        }
    }
}
TOP

Related Classes of org.locationtech.udig.document.ui.DocumentView$DocumentViewContentProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.