org.eclipse.m2e.editor.dialogs.ManageDependenciesDialog.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.m2e.editor.dialogs.ManageDependenciesDialog.java

Source

/*******************************************************************************
 * Copyright (c) 2010 Sonatype, Inc.
 * 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
 *******************************************************************************/

package org.eclipse.m2e.editor.dialogs;

import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.ARTIFACT_ID;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.DEPENDENCIES;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.DEPENDENCY;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.DEPENDENCY_MANAGEMENT;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.GROUP_ID;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.VERSION;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.findChild;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.findChilds;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.getChild;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.getTextValue;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.performOnDOMDocument;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.removeChild;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
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.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;

import org.apache.maven.model.Dependency;
import org.apache.maven.project.MavenProject;

import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.ui.internal.components.PomHierarchyComposite;
import org.eclipse.m2e.core.ui.internal.dialogs.AbstractMavenDialog;
import org.eclipse.m2e.core.ui.internal.editing.PomEdits.CompoundOperation;
import org.eclipse.m2e.core.ui.internal.editing.PomEdits.Operation;
import org.eclipse.m2e.core.ui.internal.editing.PomEdits.OperationTuple;
import org.eclipse.m2e.core.ui.internal.editing.PomHelper;
import org.eclipse.m2e.editor.MavenEditorPlugin;
import org.eclipse.m2e.editor.composites.DependencyLabelProvider;
import org.eclipse.m2e.editor.composites.ListEditorContentProvider;
import org.eclipse.m2e.editor.pom.ValueProvider;

/**
 * This dialog is used to present the user with a list of dialogs that they can move to being managed under
 * "dependencyManagement". It allows them to pick the destination POM where the dependencies will be managed.
 * 
 * @author rgould
 */
public class ManageDependenciesDialog extends AbstractMavenDialog {
    private static final Logger LOG = LoggerFactory.getLogger(ManageDependenciesDialog.class);

    private static final String DIALOG_SETTINGS = ManageDependenciesDialog.class.getName();

    private TableViewer dependenciesViewer;

    private final LinkedList<MavenProject> projectHierarchy;

    private PomHierarchyComposite pomHierarchy;

    private IStatus status;

    private List<Object> originalSelection;

    private ValueProvider<List<Dependency>> modelVProvider;

    /**
     * Hierarchy is a LinkedList representing the hierarchy relationship between POM represented by model and its parents.
     * The head of the list should be the child, while the tail should be the root parent, with the others in between.
     */
    public ManageDependenciesDialog(Shell parent, ValueProvider<List<Dependency>> modelVProvider,
            LinkedList<MavenProject> hierarchy) {
        this(parent, modelVProvider, hierarchy, null);
    }

    public ManageDependenciesDialog(Shell parent, ValueProvider<List<Dependency>> modelVProvider,
            LinkedList<MavenProject> hierarchy, List<Object> selection) {
        super(parent, DIALOG_SETTINGS);

        setShellStyle(getShellStyle() | SWT.RESIZE);
        setTitle(Messages.ManageDependenciesDialog_dialogTitle);

        this.projectHierarchy = hierarchy;
        this.originalSelection = selection;
        this.modelVProvider = modelVProvider;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
     */
    protected Control createDialogArea(Composite parent) {
        readSettings();

        Composite composite = (Composite) super.createDialogArea(parent);

        Label infoLabel = new Label(composite, SWT.WRAP);
        infoLabel.setText(Messages.ManageDependenciesDialog_dialogInfo);

        Label horizontalBar = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);

        SashForm sashForm = new SashForm(composite, SWT.SMOOTH | SWT.HORIZONTAL);
        Composite dependenciesComposite = new Composite(sashForm, SWT.NONE);

        Label selectDependenciesLabel = new Label(dependenciesComposite, SWT.NONE);
        selectDependenciesLabel.setText(Messages.ManageDependenciesDialog_selectDependenciesLabel);

        final Table dependenciesTable = new Table(dependenciesComposite, SWT.FLAT | SWT.MULTI | SWT.BORDER);
        final TableColumn column = new TableColumn(dependenciesTable, SWT.NONE);
        dependenciesTable.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(ControlEvent e) {
                column.setWidth(dependenciesTable.getClientArea().width);
            }
        });

        Composite pomComposite = new Composite(sashForm, SWT.NONE);

        Label selectPomLabel = new Label(pomComposite, SWT.NONE);
        selectPomLabel.setText(Messages.ManageDependenciesDialog_selectPOMLabel);

        pomHierarchy = new PomHierarchyComposite(pomComposite, SWT.BORDER);
        pomHierarchy.setHierarchy(getProjectHierarchy());
        // pomsViewer = new TreeViewer(pomComposite, SWT.BORDER);

        /*
         * Configure layouts
         */

        GridLayout layout = new GridLayout(1, false);
        composite.setLayout(layout);

        GridData gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
        gridData.widthHint = 300;
        infoLabel.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
        horizontalBar.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        sashForm.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        dependenciesComposite.setLayoutData(gridData);

        layout = new GridLayout(1, false);
        dependenciesComposite.setLayout(layout);

        gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
        selectDependenciesLabel.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        dependenciesTable.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        pomComposite.setLayoutData(gridData);

        layout = new GridLayout(1, false);
        pomComposite.setLayout(layout);

        gridData = new GridData(SWT.FILL, SWT.NONE, true, false);
        selectPomLabel.setLayoutData(gridData);

        gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
        pomHierarchy.setLayoutData(gridData);
        //pomsViewer.getTree().setLayoutData(gridData);

        /*
         * Set up list/tree viewers
         */

        dependenciesViewer = new TableViewer(dependenciesTable);
        dependenciesViewer.setLabelProvider(new DependencyLabelProvider());
        dependenciesViewer.setContentProvider(new ListEditorContentProvider<Dependency>());
        //MNGECLIPSE-2675 only show the dependencies not already managed (decide just by absence of the version element
        List<Dependency> deps = modelVProvider.getValue();
        List<Dependency> nonManaged = new ArrayList<Dependency>();
        if (deps != null) {
            for (Dependency d : deps) {
                if (d.getVersion() != null) {
                    nonManaged.add(d);
                }
            }
        }
        dependenciesViewer.setInput(nonManaged);
        dependenciesViewer.addSelectionChangedListener(new DependenciesViewerSelectionListener());

        pomHierarchy.addSelectionChangedListener(new PomViewerSelectionChangedListener());
        if (getProjectHierarchy().size() > 0) {
            pomHierarchy.setSelection(new StructuredSelection(getProjectHierarchy().getLast()));
        }

        if (originalSelection != null && originalSelection.size() > 0) {
            dependenciesViewer.setSelection(new StructuredSelection(originalSelection));
        }

        return composite;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
     */
    protected void computeResult() {
        MavenProject targetPOM = getTargetPOM();
        IMavenProjectFacade targetFacade = MavenPlugin.getMavenProjectRegistry()
                .getMavenProject(targetPOM.getGroupId(), targetPOM.getArtifactId(), targetPOM.getVersion());
        MavenProject currentPOM = projectHierarchy.getFirst();
        IMavenProjectFacade currentFacade = MavenPlugin.getMavenProjectRegistry()
                .getMavenProject(currentPOM.getGroupId(), currentPOM.getArtifactId(), currentPOM.getVersion());

        if (targetFacade == null || currentFacade == null) {
            return;
        }
        final boolean same = targetPOM.equals(currentPOM);

        final LinkedList<Dependency> modelDeps = getDependenciesList();

        /*
         * 1) Remove version values from the dependencies from the current POM
         * 2) Add dependencies to dependencyManagement of targetPOM
         */

        //First we remove the version from the original dependency
        final IFile current = currentFacade.getPom();
        final IFile target = targetFacade.getPom();
        Job perform = new Job("Updating POM file(s)") {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                try {
                    if (same) {
                        performOnDOMDocument(new OperationTuple(current, new CompoundOperation(
                                createManageOperation(modelDeps), createRemoveVersionOperation(modelDeps))));
                    } else {
                        performOnDOMDocument(new OperationTuple(target, createManageOperation(modelDeps)),
                                new OperationTuple(current, createRemoveVersionOperation(modelDeps)));
                    }
                } catch (Exception e) {
                    LOG.error("Error updating managed dependencies", e);
                    return new Status(IStatus.ERROR, MavenEditorPlugin.PLUGIN_ID,
                            "Error updating managed dependencies", e);
                }
                return Status.OK_STATUS;
            }
        };
        perform.setUser(false);
        perform.setSystem(true);
        perform.schedule();
    }

    public static Operation createRemoveVersionOperation(final List<Dependency> modelDeps) {
        return new Operation() {
            public void process(Document document) {
                List<Element> deps = findChilds(findChild(document.getDocumentElement(), DEPENDENCIES), DEPENDENCY);
                for (Element dep : deps) {
                    String grid = getTextValue(findChild(dep, GROUP_ID));
                    String artid = getTextValue(findChild(dep, ARTIFACT_ID));
                    for (Dependency modelDep : modelDeps) {
                        if (modelDep.getGroupId() != null && modelDep.getGroupId().equals(grid)
                                && modelDep.getArtifactId() != null && modelDep.getArtifactId().equals(artid)) {
                            removeChild(dep, findChild(dep, VERSION));
                        }
                    }
                }
            }
        };

    }

    public static Operation createManageOperation(final List<Dependency> modelDeps) {
        return new Operation() {
            public void process(Document document) {
                List<Dependency> modelDependencies = new ArrayList<Dependency>(modelDeps);
                Element managedDepsElement = getChild(document.getDocumentElement(), DEPENDENCY_MANAGEMENT,
                        DEPENDENCIES);
                List<Element> existing = findChilds(managedDepsElement, DEPENDENCY);
                for (Element dep : existing) {
                    String artifactId = getTextValue(findChild(dep, ARTIFACT_ID));
                    String groupId = getTextValue(findChild(dep, GROUP_ID));
                    //cloned list, shall not modify shared resource (used by the remove operation)
                    Iterator<Dependency> mdIter = modelDependencies.iterator();
                    while (mdIter.hasNext()) {
                        //TODO: here we iterate to find existing managed dependencies and decide not to overwrite them.
                        // but this could eventually break the current project when the versions are diametrally different
                        // we should have shown this information to the user in the UI in the first place (for him to decide what to do)
                        Dependency md = mdIter.next();
                        if (artifactId.equals(md.getArtifactId()) && groupId.equals(md.getGroupId())) {
                            mdIter.remove();
                            break;
                        }
                    }
                }
                //TODO is the version is defined by property expression, we should make sure the property is defined in the current project
                for (Dependency modelDependency : modelDependencies) {
                    PomHelper.createDependency(managedDepsElement, modelDependency.getGroupId(),
                            modelDependency.getArtifactId(), modelDependency.getVersion());
                }
            }
        };

    }

    protected LinkedList<Dependency> getDependenciesList() {
        IStructuredSelection selection = (IStructuredSelection) dependenciesViewer.getSelection();

        LinkedList<Dependency> dependencies = new LinkedList<Dependency>();

        for (Object obj : selection.toArray()) {
            dependencies.add((Dependency) obj);
        }

        return dependencies;
    }

    protected LinkedList<MavenProject> getProjectHierarchy() {
        return this.projectHierarchy;
    }

    protected MavenProject getTargetPOM() {
        IStructuredSelection selection = (IStructuredSelection) pomHierarchy.getSelection();
        return (MavenProject) selection.getFirstElement();
    }

    /**
     * Compare the list of selected dependencies against the selected targetPOM. If one of the dependencies is already
     * under dependencyManagement, but has a different version than the selected dependency, warn the user about this.
     * returns true if the user has been warned (but this method updates the status itself)
     * 
     * @param model
     * @param dependencies
     */
    protected boolean checkDependencies(org.apache.maven.model.Model model, LinkedList<Dependency> dependencies) {
        if (this.status != null && this.status.getCode() == IStatus.ERROR) {
            //Don't warn the user if there is already an error
            return false;
        }
        if (model == null || model.getDependencyManagement() == null
                || model.getDependencyManagement().getDependencies() == null
                || model.getDependencyManagement().getDependencies().isEmpty()) {
            return false;
        }

        for (Dependency selectedDep : dependencies) {
            for (org.apache.maven.model.Dependency targetDep : model.getDependencyManagement().getDependencies()) {
                if (selectedDep.getGroupId().equals(targetDep.getGroupId())
                        && selectedDep.getArtifactId().equals(targetDep.getArtifactId())
                        && !selectedDep.getVersion().equals(targetDep.getVersion())) {
                    String modelID = model.getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion(); //$NON-NLS-1$ //$NON-NLS-2$
                    if (targetDep.getLocation("") != null && targetDep.getLocation("").getSource() != null) { //$NON-NLS-1$ //$NON-NLS-2$
                        modelID = targetDep.getLocation("").getSource().getModelId(); //$NON-NLS-1$
                    }
                    Object[] arguments = { selectedDep.getArtifactId() + "-" + selectedDep.getVersion(), //$NON-NLS-1$
                            targetDep.getVersion(), modelID };
                    String message = NLS.bind(Messages.ManageDependenciesDialog_dependencyExistsWarning, arguments);
                    updateStatus(new Status(IStatus.WARNING, MavenEditorPlugin.PLUGIN_ID, message));
                    return true;
                }
            }
        }
        return false;
    }

    protected void checkStatus(MavenProject targetProject, LinkedList<Dependency> selectedDependencies) {
        if (targetProject == null || selectedDependencies.isEmpty()) {
            updateStatus(new Status(IStatus.ERROR, MavenEditorPlugin.PLUGIN_ID,
                    Messages.ManageDependenciesDialog_emptySelectionError));
            return;
        }
        boolean error = false;
        IMavenProjectFacade facade = MavenPlugin.getMavenProjectRegistry().getMavenProject(
                targetProject.getGroupId(), targetProject.getArtifactId(), targetProject.getVersion());
        if (facade == null) {
            error = true;
            updateStatus(new Status(IStatus.ERROR, MavenEditorPlugin.PLUGIN_ID,
                    Messages.ManageDependenciesDialog_projectNotPresentError));
        } else {
            org.apache.maven.model.Model model = null;
            if (facade.getMavenProject() == null || facade.getMavenProject().getModel() == null) {
                try {
                    model = MavenPlugin.getMavenModelManager().readMavenModel(facade.getPom());
                } catch (CoreException e) {
                    Object[] arguments = { facade.getPom(), e.getLocalizedMessage() };
                    String message = NLS.bind(Messages.ManageDependenciesDialog_pomReadingError, arguments);
                    Status status = new Status(IStatus.ERROR, MavenEditorPlugin.PLUGIN_ID, message);
                    LOG.info(message, e);
                    updateStatus(status);
                    error = true;
                }
            } else {
                model = facade.getMavenProject().getModel();
            }
            if (model != null) {
                error = checkDependencies(model, getDependenciesList());
            }
        }

        if (!error) {
            clearStatus();
        }
    }

    protected void clearStatus() {
        updateStatus(new Status(IStatus.OK, MavenEditorPlugin.PLUGIN_ID, "")); //$NON-NLS-1$
    }

    protected class DependenciesViewerSelectionListener implements ISelectionChangedListener {
        public void selectionChanged(SelectionChangedEvent event) {
            checkStatus(getTargetPOM(), getDependenciesList());
        }
    }

    protected class PomViewerSelectionChangedListener implements ISelectionChangedListener {
        public void selectionChanged(SelectionChangedEvent event) {
            checkStatus(getTargetPOM(), getDependenciesList());
        }
    }

    @Override
    protected void updateStatus(IStatus status) {
        this.status = status;
        super.updateStatus(status);
    }

    public static class DepLabelProvider extends LabelProvider implements IColorProvider {
        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
         */
        @Override
        public String getText(Object element) {
            MavenProject project = null;
            if (element instanceof MavenProject) {
                project = (MavenProject) element;
            } else if (element instanceof Object[]) {
                project = (MavenProject) ((Object[]) element)[0];
            } else {
                return ""; //$NON-NLS-1$
            }

            StringBuffer buffer = new StringBuffer();
            buffer.append(project.getGroupId() + " : " + project.getArtifactId() + " : " + project.getVersion()); //$NON-NLS-1$ //$NON-NLS-2$
            return buffer.toString();

        }

        public Color getForeground(Object element) {
            if (element instanceof MavenProject) {
                MavenProject project = (MavenProject) element;
                IMavenProjectFacade search = MavenPlugin.getMavenProjectRegistry()
                        .getMavenProject(project.getGroupId(), project.getArtifactId(), project.getVersion());
                if (search == null) {
                    //This project is not in the workspace so we can't really modify it.
                    return Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY);
                }
            }
            return null;
        }

        public Color getBackground(Object element) {
            return null;
        }
    }

    public class ContentProvider implements ITreeContentProvider {
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }

        public void dispose() {
        }

        public boolean hasChildren(Object element) {
            Object[] children = getChildren(element);

            return children.length != 0;
        }

        public Object getParent(Object element) {
            if (element instanceof MavenProject) {
                MavenProject project = (MavenProject) element;
                return project.getParent();
            }
            return null;
        }

        /*
         * Return root element
         * (non-Javadoc)
         * @see org.eclipse.jface.viewers.ITreeContentProvider#getElements(java.lang.Object)
         */
        public Object[] getElements(Object inputElement) {

            if (inputElement instanceof LinkedList) {
                LinkedList<MavenProject> projects = (LinkedList<MavenProject>) inputElement;
                if (projects.isEmpty()) {
                    return new Object[0];
                }
                return new Object[] { projects.getLast() };
            }

            return new Object[0];
        }

        public Object[] getChildren(Object parentElement) {
            if (parentElement instanceof MavenProject) {
                /*
                 * Walk the hierarchy list until we find the parentElement and
                 * return the previous element, which is the child.
                 */
                MavenProject parent = (MavenProject) parentElement;

                if (getProjectHierarchy().size() == 1) {
                    //No parent exists, only one element in the tree
                    return new Object[0];
                }

                if (getProjectHierarchy().getFirst().equals(parent)) {
                    //We are the final child
                    return new Object[0];
                }

                ListIterator<MavenProject> iter = getProjectHierarchy().listIterator();
                while (iter.hasNext()) {
                    MavenProject next = iter.next();
                    if (next.equals(parent)) {
                        iter.previous();
                        MavenProject previous = iter.previous();
                        return new Object[] { previous };
                    }
                }
            }
            return new Object[0];
        }
    }
}