/** * Copyright (C) 2005 - 2013 Eric Van Dewoestine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.eclim.installer.step; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.eclim.installer.step.command.Command; import org.eclim.installer.step.command.InstallCommand; import org.eclim.installer.step.command.ListCommand; import org.eclim.installer.step.command.OutputHandler; import org.eclim.installer.theme.DesertBlue; import org.formic.Installer; import org.formic.util.dialog.gui.GuiDialogs; import org.formic.wizard.step.gui.InstallStep; import com.jgoodies.looks.plastic.PlasticTheme; import foxtrot.Worker; /** * Step which installs necessary third party eclipse plugins. * * @author Eric Van Dewoestine */ public class EclipsePluginsStep extends InstallStep implements OutputHandler { private static final String BEGIN_TASK = "beginTask"; private static final String PREPARE_TASK = "prepare"; private static final String SUB_TASK = "subTask"; private static final String INTERNAL_WORKED = "worked"; private static final String SET_TASK_NAME = "setTaskName"; private static final Color ERROR_COLOR = new Color(255, 201, 201); private String taskName = ""; private JPanel stepPanel; private JPanel featuresPanel; private JLabel messageLabel; private ImageIcon errorIcon; private DefaultTableModel tableModel; private List<Dependency> dependencies; private Map<String,String> availableFeatures; private PlasticTheme theme; private int overallProgressStep; /** * Constructs this step. */ public EclipsePluginsStep(String name, Properties properties) { super(name, properties); } @Override public Component init() { theme = new DesertBlue(); stepPanel = (JPanel)super.init(); stepPanel.setBorder(null); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); messageLabel = new JLabel(); messageLabel.setPreferredSize(new Dimension(25, 25)); panel.add(messageLabel); panel.add(stepPanel); panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 10, 25)); return panel; } private void setMessage(String message) { if(errorIcon == null){ errorIcon = new ImageIcon(Installer.getImage("form.error.icon")); } if(message != null){ messageLabel.setIcon(errorIcon); messageLabel.setText(message); }else{ messageLabel.setIcon(null); messageLabel.setText(null); } } @Override public void displayed() { setBusy(true); setPreviousEnabled(false); try{ overallLabel.setText(""); overallProgress.setValue(0); taskLabel.setText(""); taskProgress.setValue(0); taskProgress.setIndeterminate(true); // handle step re-entry. if (featuresPanel != null){ stepPanel.remove(featuresPanel); } EclipseInfo info = (EclipseInfo) Installer.getContext().getValue("eclipse.info"); // find chosen features dependencies which need to be installed/upgraded. dependencies = unsatisfiedDependencies(info); if(dependencies.size() == 0){ overallProgress.setMaximum(1); overallProgress.setValue(1); overallLabel.setText("All third party plugins are up to date."); taskProgress.setMaximum(1); taskProgress.setValue(1); taskLabel.setText(""); }else{ tableModel = new DefaultTableModel(); tableModel.addColumn("Feature"); tableModel.addColumn("Version"); tableModel.addColumn("Install / Upgrade"); JTable table = new JTable(tableModel); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.setDefaultRenderer(Object.class, new DependencyCellRenderer()); table.getSelectionModel().addListSelectionListener( new DependencySelectionListener()); featuresPanel = new JPanel(new BorderLayout()); featuresPanel.setAlignmentX(0.0f); JPanel container = new JPanel(new BorderLayout()); container.add(table, BorderLayout.CENTER); JScrollPane scrollPane = new JScrollPane(container); scrollPane.setAlignmentX(0.0f); availableFeatures = loadAvailableFeatures(); for (Dependency dependency : dependencies){ String version = availableFeatures.get(dependency.getId()); String manual = ""; if (version == null){ manual = " (Manual)"; version = dependency.getRequiredVersion(); } tableModel.addRow(new Object[]{ dependency.getId(), version, (dependency.isUpgrade() ? "Upgrade" : "Install") + manual, }); } JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0)); buttons.setAlignmentX(0.0f); JButton skipButton = new JButton(new SkipPluginsAction()); JButton installButton = new JButton(new InstallPluginsAction(skipButton)); buttons.add(installButton); buttons.add(skipButton); featuresPanel.add(scrollPane, BorderLayout.CENTER); featuresPanel.add(buttons, BorderLayout.SOUTH); stepPanel.add(featuresPanel); overallProgress.setValue(0); overallLabel.setText(""); taskProgress.setValue(0); taskLabel.setText(""); } }catch(Exception e){ setError(e); }finally{ setValid(dependencies != null && dependencies.size() == 0); setBusy(false); setPreviousEnabled(true); taskProgress.setIndeterminate(false); } } public void process(final String line) { SwingUtilities.invokeLater(new Runnable(){ public void run(){ if (line.startsWith(BEGIN_TASK)){ String l = line.substring(BEGIN_TASK.length() + 2); double work = Double.parseDouble( l.substring(l.indexOf('=') + 1, l.indexOf(' '))); taskProgress.setIndeterminate(false); taskProgress.setMaximum((int)work); taskProgress.setValue(0); }else if(line.startsWith(PREPARE_TASK)){ taskLabel.setText(line.substring(PREPARE_TASK.length() + 1).trim()); }else if(line.startsWith(SUB_TASK)){ taskLabel.setText( taskName + line.substring(SUB_TASK.length() + 1).trim()); }else if(line.startsWith(INTERNAL_WORKED)){ double worked = Double.parseDouble( line.substring(INTERNAL_WORKED.length() + 2)); taskProgress.setValue((int)worked); overallProgress.setValue( overallProgressStep + (int)(taskProgress.getPercentComplete() * 100)); }else if(line.startsWith(SET_TASK_NAME)){ taskName = line.substring(SET_TASK_NAME.length() + 1).trim() + ' '; } } }); } private List<Dependency> unsatisfiedDependencies(EclipseInfo info) { List<Dependency> deps = new ArrayList<Dependency>(); String[] features = Installer.getContext().getKeysByPrefix("featureList"); Arrays.sort(features, new FeatureNameComparator()); for (int ii = 0; ii < features.length; ii++){ Boolean enabled = (Boolean)Installer.getContext().getValue(features[ii]); if (enabled.booleanValue()){ String name = features[ii].substring(features[ii].indexOf('.') + 1); deps.addAll(info.getUnsatisfiedDependencies(name)); } } return deps; } @SuppressWarnings("unchecked") private Map<String,String> loadAvailableFeatures() throws Exception { // load up available features from update sites. overallProgress.setMaximum(1); overallLabel.setText("Loading available features from update sites..."); return (Map<String,String>)Worker.post(new foxtrot.Task(){ public Object run() throws Exception { final Map<String,String> availableFeatures = new HashMap<String,String>(); ArrayList<String> sites = new ArrayList<String>(); for (Dependency dependency : dependencies){ String site = dependency.getSite(); if (site == null){ availableFeatures.put( dependency.getId(), dependency.getRequiredVersion()); }else if (!sites.contains(site)){ sites.add(site); } } OutputHandler handler = new OutputHandler(){ public void process(String line){ String[] parts = StringUtils.split(line, "="); if (parts.length == 2 && parts[0].endsWith(".feature.group")){ availableFeatures.put( parts[0].replace(".feature.group", ""), parts[1]); } } }; if (sites.size() > 0){ Command command = new ListCommand( handler, sites.toArray(new String[sites.size()])); try{ command.start(); command.join(); if(command.getReturnCode() != 0){ throw new RuntimeException( "error: " + command.getErrorMessage() + " out: " + command.getResult()); } }finally{ command.destroy(); } } return availableFeatures; } }); } private class InstallPluginsAction extends AbstractAction { private static final long serialVersionUID = 1L; private JButton skipButton; public InstallPluginsAction(JButton skipButton) { super("Install Features"); this.skipButton = skipButton; } public void actionPerformed(ActionEvent e) { ((JButton)e.getSource()).setEnabled(false); setPreviousEnabled(true); try{ Boolean successful = (Boolean)Worker.post(new foxtrot.Task(){ public Object run() throws Exception { // check if any of the features cannot be installed. for (Dependency dependency : dependencies){ Feature feature = dependency.getFeature(); if (feature != null) { if (feature.getSite() == null){ String installLog = FilenameUtils.concat( SystemUtils.JAVA_IO_TMPDIR, "install.log"); GuiDialogs.showWarning(Installer.getString( "eclipsePlugins.install.features.site.not.found", installLog)); return Boolean.FALSE; }else if (!feature.getSite().canWrite()){ GuiDialogs.showWarning(Installer.getString( "eclipsePlugins.install.features.permission.denied")); return Boolean.FALSE; } } } int installCount = 0; for (Iterator<Dependency> ii = dependencies.iterator(); ii.hasNext();){ Dependency dependency = ii.next(); if (dependency.getSite() != null){ installCount++; } } overallProgress.setMaximum(installCount * 100); overallProgress.setValue(0); int removeIndex = 0; for (Iterator<Dependency> ii = dependencies.iterator(); ii.hasNext();){ Dependency dependency = ii.next(); String site = dependency.getSite(); String version = availableFeatures.get(dependency.getId()); if (site != null && version != null){ if(!dependency.isUpgrade()){ overallLabel.setText("Installing feature: " + dependency.getId() + '-' + version); }else{ overallLabel.setText("Updating feature: " + dependency.getId() + '-' + version); } taskProgress.setValue(0); taskProgress.setIndeterminate(true); Command command = new InstallCommand( EclipsePluginsStep.this, dependency.getSite(), dependency.getId()); try{ command.start(); command.join(); if(command.getReturnCode() != 0){ if (command.isShutdown()){ return Boolean.TRUE; } throw new RuntimeException( "error: " + command.getErrorMessage() + " out: " + command.getResult()); } }finally{ command.destroy(); } ii.remove(); tableModel.removeRow(removeIndex); }else{ removeIndex++; } overallProgressStep += 100; overallProgress.setValue(overallProgressStep); } taskLabel.setText(""); taskProgress.setValue(taskProgress.getMaximum()); overallProgress.setValue(overallProgress.getMaximum()); if(dependencies.size() > 0){ GuiDialogs.showWarning(Installer.getString("eclipsePlugins.manual")); } return Boolean.TRUE; } }); if(successful.booleanValue()){ overallProgress.setValue(overallProgress.getMaximum()); overallLabel.setText(Installer.getString("install.done")); taskProgress.setValue(taskProgress.getMaximum()); taskLabel.setText(Installer.getString("install.done")); setBusy(false); setValid(true); taskProgress.setIndeterminate(false); skipButton.setEnabled(false); } }catch(Exception ex){ setError(ex); } } } private class SkipPluginsAction extends AbstractAction { private static final long serialVersionUID = 1L; public SkipPluginsAction() { super("Skip"); } public void actionPerformed(ActionEvent e) { if(GuiDialogs.showConfirm(Installer.getString("eclipsePlugins.skip"))){ setValid(true); } } } private class DependencyCellRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component component = super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column); Feature feature = ((Dependency)dependencies.get(row)).getFeature(); if (feature != null && (feature.getSite() == null || !feature.getSite().canWrite())) { component.setBackground(isSelected ? theme.getMenuItemSelectedBackground() : ERROR_COLOR); component.setForeground(isSelected ? ERROR_COLOR : Color.BLACK); }else{ component.setBackground(isSelected ? theme.getMenuItemSelectedBackground() : Color.WHITE); component.setForeground(isSelected ? theme.getMenuItemSelectedForeground() : theme.getMenuForeground()); } return component; } } /** * Mouse listener for the feature list. */ private class DependencySelectionListener implements ListSelectionListener { @Override public void valueChanged(ListSelectionEvent e) { ListSelectionModel model = (ListSelectionModel)e.getSource(); int index = model.getMinSelectionIndex(); Feature feature = index >= 0 ? ((Dependency)dependencies.get(index)).getFeature() : null; if (feature != null) { if (feature.getSite() == null){ setMessage(Installer.getString("eclipsePlugins.upgrade.site.not.found")); }else if (!feature.getSite().canWrite()){ setMessage(Installer.getString( "eclipsePlugins.upgrade.permission.denied")); } }else{ setMessage(null); } } } private static class FeatureNameComparator implements Comparator<String> { private static ArrayList<String> NAMES = new ArrayList<String>(); static{ NAMES.add("featureList.jdt"); NAMES.add("featureList.wst"); NAMES.add("featureList.adt"); NAMES.add("featureList.cdt"); NAMES.add("featureList.pdt"); NAMES.add("featureList.pydev"); NAMES.add("featureList.sdt210"); NAMES.add("featureList.groovy"); } public int compare(String ob1, String ob2) { return NAMES.indexOf(ob1) - NAMES.indexOf(ob2); } } }