/* * Copyright (c) 2007-2009, Osmorc Development Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list * of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this * list of conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be * used to endorse or promote products derived from this software without specific * prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.osmorc.run.ui; import com.intellij.execution.ui.DefaultJreSelector; import com.intellij.execution.ui.JrePathEditor; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.CompilerProjectExtension; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.ui.RawCommandLineEditor; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TableSpeedSearch; import com.intellij.ui.ToolbarDecorator; import org.jetbrains.annotations.NotNull; import org.osmorc.frameworkintegration.FrameworkInstanceDefinition; import org.osmorc.frameworkintegration.FrameworkIntegrator; import org.osmorc.frameworkintegration.FrameworkIntegratorRegistry; import org.osmorc.i18n.OsmorcBundle; import org.osmorc.run.OsgiRunConfiguration; import org.osmorc.run.OsgiRunConfigurationChecker; import org.osmorc.run.OsgiRunConfigurationCheckerProvider; import org.osmorc.settings.ApplicationSettings; import org.osmorc.util.OsgiUiUtil; import javax.swing.*; import javax.swing.table.AbstractTableModel; import javax.swing.text.DefaultFormatterFactory; import java.awt.*; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.stream.IntStream; /** * Editor for a bundle run configuration. * * @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a> * @author <a href="mailto:robert@beeger.net">Robert F. Beeger</a> */ public class OsgiRunConfigurationEditor extends SettingsEditor<OsgiRunConfiguration> { private JTabbedPane root; // framework& bundles tab private JComboBox<FrameworkInstanceDefinition> myFrameworkInstances; private JSpinner myFrameworkStartLevel; private JSpinner myDefaultStartLevel; private JPanel myBundlesPanel; private JTable myBundlesTable; // parameters tab private RawCommandLineEditor myVmOptions; private RawCommandLineEditor myProgramParameters; private JRadioButton myOsmorcControlledDir; private JRadioButton myUserDefinedDir; private TextFieldWithBrowseButton myWorkingDirField; private JrePathEditor myJrePathEditor; private JCheckBox myClassPathAllBundles; // additional properties tab private JPanel myAdditionalPropertiesPanel; private final Project myProject; private OsgiRunConfiguration myRunConfiguration; private FrameworkRunPropertiesEditor myCurrentRunPropertiesEditor; public OsgiRunConfigurationEditor(final Project project) { myProject = project; myJrePathEditor.setDefaultJreSelector(DefaultJreSelector.projectSdk(project)); myFrameworkStartLevel.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1)); JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor)myFrameworkStartLevel.getEditor(); editor.getTextField().setFormatterFactory(new DefaultFormatterFactory(new JSpinnerCellEditor.MyNumberFormatter("Auto"))); myDefaultStartLevel.setModel(new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1)); DefaultComboBoxModel<FrameworkInstanceDefinition> model = new DefaultComboBoxModel<>(); model.addElement(null); for (FrameworkInstanceDefinition instance : ApplicationSettings.getInstance().getActiveFrameworkInstanceDefinitions()) { model.addElement(instance); } myFrameworkInstances.setModel(model); myFrameworkInstances.addActionListener((e) -> onFrameworkChange()); myFrameworkInstances.setRenderer(new OsgiUiUtil.FrameworkInstanceRenderer("[project default]")); myBundlesTable.setModel(new RunConfigurationTableModel()); myBundlesTable.setRowSelectionAllowed(true); myBundlesTable.setColumnSelectionAllowed(false); myBundlesTable.setDefaultEditor(Integer.class, new JSpinnerCellEditor()); myBundlesTable.setDefaultRenderer(Integer.class, new JSpinnerCellEditor()); myBundlesTable.setAutoCreateRowSorter(true); myBundlesPanel.add( ToolbarDecorator.createDecorator(myBundlesTable) .setAddAction((b) -> onAddClick()) .setRemoveAction((b) -> onRemoveClick()) .disableUpDownActions() .createPanel(), BorderLayout.CENTER ); myBundlesTable.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { int width = myBundlesTable.getWidth(); if (width > 200) { myBundlesTable.getColumnModel().getColumn(0).setPreferredWidth(width - 200); myBundlesTable.getColumnModel().getColumn(1).setPreferredWidth(80); myBundlesTable.getColumnModel().getColumn(2).setPreferredWidth(120); } } }); new TableSpeedSearch(myBundlesTable).setComparator(new SpeedSearchComparator(false)); myOsmorcControlledDir.addChangeListener((e) -> { boolean isUserDefined = !myOsmorcControlledDir.isSelected(); myWorkingDirField.setEnabled(isUserDefined); }); FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); String title = OsmorcBundle.message("run.configuration.working.dir.title"); String description = OsmorcBundle.message("run.configuration.working.dir.description"); myWorkingDirField.addBrowseFolderListener(title, description, null, descriptor); myWorkingDirField.getTextField().setColumns(30); // avoid text fields growing the dialog when much text is entered. myVmOptions.getTextField().setPreferredSize(new Dimension(100, 20)); myProgramParameters.getTextField().setPreferredSize(new Dimension(100, 20)); } /** * Called when the framework is changed. This will create a new editor for framework properties and will also remove * any framework bundles from the list, as they are no longer in classpath. */ private void onFrameworkChange() { if (myFrameworkInstances.getSelectedItem() != null) { FrameworkInstanceDefinition instance = (FrameworkInstanceDefinition)myFrameworkInstances.getSelectedItem(); FrameworkIntegrator integrator = FrameworkIntegratorRegistry.getInstance().findIntegratorByInstanceDefinition(instance); assert integrator != null : instance; // clear the panel myAdditionalPropertiesPanel.removeAll(); // create and install a new editor (if present) myCurrentRunPropertiesEditor = integrator.createRunPropertiesEditor(); if (myCurrentRunPropertiesEditor != null) { myAdditionalPropertiesPanel.removeAll(); myAdditionalPropertiesPanel.add(myCurrentRunPropertiesEditor.getUI(), BorderLayout.CENTER); if (myRunConfiguration != null) { myCurrentRunPropertiesEditor.resetEditorFrom(myRunConfiguration); OsgiRunConfigurationChecker checker = null; if (integrator instanceof OsgiRunConfigurationCheckerProvider) { checker = ((OsgiRunConfigurationCheckerProvider)integrator).getOsgiRunConfigurationChecker(); } myRunConfiguration.setAdditionalChecker(checker); } } // remove all framework bundles from the list RunConfigurationTableModel model = getTableModel(); model.removeAllOfType(SelectedBundle.BundleType.FrameworkBundle); } } private RunConfigurationTableModel getTableModel() { return (RunConfigurationTableModel)myBundlesTable.getModel(); } private void onAddClick() { FrameworkInstanceDefinition instance = (FrameworkInstanceDefinition)myFrameworkInstances.getSelectedItem(); BundleSelector selector = new BundleSelector(myProject, instance, getBundlesToRun()); if (selector.showAndGet()) { RunConfigurationTableModel model = getTableModel(); for (SelectedBundle aModule : selector.getSelectedBundles()) { model.addBundle(aModule); } } } private void onRemoveClick() { int[] indices = IntStream.of(myBundlesTable.getSelectedRows()).map(myBundlesTable::convertRowIndexToModel).sorted().toArray(); RunConfigurationTableModel model = getTableModel(); for (int i = indices.length - 1; i >= 0; i--) { model.removeBundleAt(indices[i]); } } @Override protected void resetEditorFrom(@NotNull OsgiRunConfiguration osgiRunConfiguration) { myRunConfiguration = osgiRunConfiguration; myVmOptions.setText(osgiRunConfiguration.getVmParameters()); myProgramParameters.setText(osgiRunConfiguration.getProgramParameters()); myFrameworkInstances.setSelectedItem(osgiRunConfiguration.getInstanceToUse()); myClassPathAllBundles.setSelected(osgiRunConfiguration.isIncludeAllBundlesInClassPath()); if (myCurrentRunPropertiesEditor != null) { myCurrentRunPropertiesEditor.resetEditorFrom(osgiRunConfiguration); } // I deliberately set the list of modules as the last step here as // the framework specific modules are cleaned out when you change the framework instance // so the framework instance should be changed first List<SelectedBundle> modules = osgiRunConfiguration.getBundlesToDeploy(); RunConfigurationTableModel model = getTableModel(); while (model.getRowCount() > 0) { model.removeBundleAt(0); } for (SelectedBundle module : modules) { model.addBundle(module); } myBundlesTable.getColumnModel().getColumn(1).setPreferredWidth(200); myFrameworkStartLevel.setValue(osgiRunConfiguration.getFrameworkStartLevel()); myDefaultStartLevel.setValue(osgiRunConfiguration.getDefaultStartLevel()); boolean useUserDefinedFields = !osgiRunConfiguration.isGenerateWorkingDir(); myWorkingDirField.setText(osgiRunConfiguration.getWorkingDir()); if (myWorkingDirField.getText().length() == 0) { final CompilerProjectExtension extension = CompilerProjectExtension.getInstance(myProject); if (extension != null) { final VirtualFilePointer outputDirPointer = extension.getCompilerOutputPointer(); if (outputDirPointer != null) { myWorkingDirField.setText(VfsUtilCore.urlToPath(outputDirPointer.getUrl() + "/run.osgi/")); } } } myWorkingDirField.setEnabled(useUserDefinedFields); myUserDefinedDir.setSelected(useUserDefinedFields); myOsmorcControlledDir.setSelected(!useUserDefinedFields); myJrePathEditor.setPathOrName(osgiRunConfiguration.getAlternativeJrePath(), osgiRunConfiguration.isUseAlternativeJre()); } @Override protected void applyEditorTo(@NotNull OsgiRunConfiguration osgiRunConfiguration) throws ConfigurationException { osgiRunConfiguration.setBundlesToDeploy(getBundlesToRun()); osgiRunConfiguration.setVmParameters(myVmOptions.getText()); osgiRunConfiguration.setProgramParameters(myProgramParameters.getText()); osgiRunConfiguration.setIncludeAllBundlesInClassPath(myClassPathAllBundles.isSelected()); osgiRunConfiguration.setWorkingDir(myWorkingDirField.getText().replace('\\', '/')); osgiRunConfiguration.setUseAlternativeJre(myJrePathEditor.isAlternativeJreSelected()); osgiRunConfiguration.setAlternativeJrePath(myJrePathEditor.getJrePathOrName()); osgiRunConfiguration.setFrameworkStartLevel((Integer)myFrameworkStartLevel.getValue()); osgiRunConfiguration.setDefaultStartLevel((Integer)myDefaultStartLevel.getValue()); osgiRunConfiguration.setGenerateWorkingDir(myOsmorcControlledDir.isSelected()); osgiRunConfiguration.setInstanceToUse((FrameworkInstanceDefinition)myFrameworkInstances.getSelectedItem()); if (myCurrentRunPropertiesEditor != null) { myCurrentRunPropertiesEditor.applyEditorTo(osgiRunConfiguration); } } private List<SelectedBundle> getBundlesToRun() { return getTableModel().getBundles(); } @NotNull @Override protected JComponent createEditor() { return root; } private static class RunConfigurationTableModel extends AbstractTableModel { private final List<SelectedBundle> mySelectedBundles; public RunConfigurationTableModel() { mySelectedBundles = new ArrayList<>(); } @Override public Class<?> getColumnClass(int columnIndex) { switch (columnIndex) { case 0: return String.class; case 1: return Integer.class; case 2: return Boolean.class; default: return Object.class; } } @Override public String getColumnName(int columnIndex) { switch (columnIndex) { case 0: return "Bundle name"; case 1: return "Start level"; case 2: return "Start after install"; default: return ""; } } @Override public int getColumnCount() { return 3; } @Override public int getRowCount() { return mySelectedBundles.size(); } @Override public boolean isCellEditable(int row, int column) { return column != 0; } @Override public Object getValueAt(int row, int column) { SelectedBundle bundle = getBundleAt(row); switch (column) { case 0: return bundle.toString(); case 1: return bundle.getStartLevel(); case 2: return bundle.isStartAfterInstallation(); default: throw new RuntimeException("Don't know column " + column); } } @Override public void setValueAt(Object o, int row, int column) { SelectedBundle bundle = getBundleAt(row); switch (column) { case 1: bundle.setStartLevel((Integer)o); break; case 2: bundle.setStartAfterInstallation((Boolean)o); break; default: throw new RuntimeException("Cannot edit column " + column); } } public SelectedBundle getBundleAt(int index) { return mySelectedBundles.get(index); } public List<SelectedBundle> getBundles() { return mySelectedBundles; } public void addBundle(SelectedBundle bundle) { mySelectedBundles.add(bundle); fireTableRowsInserted(mySelectedBundles.size() - 1, mySelectedBundles.size() - 1); } public void removeBundleAt(int index) { mySelectedBundles.remove(index); fireTableRowsDeleted(index, index); } public void removeAllOfType(SelectedBundle.BundleType type) { for (Iterator<SelectedBundle> selectedBundleIterator = mySelectedBundles.iterator(); selectedBundleIterator.hasNext(); ) { SelectedBundle selectedBundle = selectedBundleIterator.next(); if (selectedBundle.getBundleType() == type) { selectedBundleIterator.remove(); } } fireTableDataChanged(); } } }