/* * Zed Attack Proxy (ZAP) and its related class files. * * ZAP is an HTTP/HTTPS proxy for assessing web application security. * * Copyright 2015 The ZAP Development Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.zaproxy.zap.extension.autoupdate; import java.awt.BorderLayout; import java.awt.Component; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.SwingConstants; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import org.apache.commons.collections.CollectionUtils; import org.jdesktop.swingx.JXTable; import org.parosproxy.paros.Constant; import org.parosproxy.paros.extension.Extension; import org.zaproxy.zap.control.AddOn; import org.zaproxy.zap.control.AddOnCollection; /** * Helper class that checks the dependency changes when an add-on, or several add-ons, are installed, uninstalled or updated. * <p> * It also allows to confirm with the user the resulting changes. * * @since 2.4.0 */ class AddOnDependencyChecker { private final AddOnCollection installedAddOns; private final AddOnCollection availableAddOns; public AddOnDependencyChecker(AddOnCollection installedAddOns, AddOnCollection availableAddOns) { this.installedAddOns = installedAddOns; this.availableAddOns = availableAddOns; } private static boolean contains(Collection<AddOn> addOns, AddOn addOn) { for (AddOn ao : addOns) { if (addOn.isSameAddOn(ao)) { return true; } } return false; } /** * Calculates the changes required to install the given add-on. * <p> * It might require updating, installing or uninstalling other add-ons depending on the dependencies of the affected * add-ons. * * @param addOn the add-on that would be installed * @return the resulting changes with the add-ons that need to be updated, installed or uninstalled */ public AddOnChangesResult calculateInstallChanges(AddOn addOn) { Set<AddOn> addOns = new HashSet<>(); addOns.add(addOn); return calculateInstallChanges(addOns); } /** * Calculates the changes required to install the given add-ons. * <p> * It might require updating, installing or uninstalling other add-ons depending on the dependencies of the affected * add-ons. * * @param selectedAddOns the add-ons that would be installed * @return the resulting changes with the add-ons that need to be updated, installed or uninstalled */ public AddOnChangesResult calculateInstallChanges(Set<AddOn> selectedAddOns) { return calculateChanges(selectedAddOns, false); } /** * Asks the user for confirmation of install changes. * * @param parent the parent component of the confirmation dialogue * @param changes the changes of the installation * @return {@code true} if the user accept the changes, {@code false} otherwise */ public boolean confirmInstallChanges(Component parent, AddOnChangesResult changes) { return confirmChanges(parent, changes, false); } private boolean addDependencies( AddOn addOn, Set<AddOn> selectedAddOns, Set<AddOn> oldVersions, Set<AddOn> newVersions, Set<AddOn> installs) { AddOn.AddOnRunRequirements requirements = addOn.calculateRunRequirements(availableAddOns.getAddOns()); for (AddOn dep : requirements.getDependencies()) { if (selectedAddOns.contains(dep)) { continue; } AddOn installed = installedAddOns.getAddOn(dep.getId()); if (installed == null) { if (AddOn.InstallationStatus.AVAILABLE == availableAddOns.getAddOn(dep.getId()).getInstallationStatus()) { installs.add(dep); } } else if (!addOn.dependsOn(installed)) { if (AddOn.InstallationStatus.AVAILABLE == availableAddOns.getAddOn(dep.getId()).getInstallationStatus()) { oldVersions.add(installed); newVersions.add(dep); } } } return requirements.isNewerJavaVersionRequired(); } private boolean confirmChanges(Component parent, AddOnChangesResult changesResult, boolean updating) { Set<AddOn> selectedAddOnsJavaIssue = new HashSet<>(); for (AddOn addOn : changesResult.getSelectedAddOns()) { if (!addOn.canRunInCurrentJavaVersion()) { selectedAddOnsJavaIssue.add(addOn); } } String question; Set<AddOn> installs = new HashSet<>(changesResult.getInstalls()); Set<AddOn> updates = new HashSet<>(changesResult.getNewVersions()); Set<AddOn> dependents = getDependents(updates, changesResult.getUninstalls()); if (updating) { question = Constant.messages.getString("cfu.confirmation.dialogue.message.continueWithUpdate"); updates.removeAll(changesResult.getSelectedAddOns()); } else { question = Constant.messages.getString("cfu.confirmation.dialogue.message.continueWithInstallation"); installs.removeAll(changesResult.getSelectedAddOns()); } if (changesResult.getUninstalls().isEmpty() && updates.isEmpty() && installs.isEmpty() && dependents.isEmpty() && changesResult.getOptionalAddOns().isEmpty() && changesResult.getSoftUnloadExtensions().isEmpty() && changesResult.getUnloadExtensions().isEmpty()) { // No other changes required. if (selectedAddOnsJavaIssue.isEmpty()) { // No need to ask for confirmation. return true; } int size = changesResult.getSelectedAddOns().size(); if (size == 1) { String baseMessage = MessageFormat.format( Constant.messages.getString("cfu.confirmation.dialogue.message.selectedAddOnNewerJavaVersion"), changesResult.getSelectedAddOns().iterator().next().getMinimumJavaVersion()); return JOptionPane.showConfirmDialog( parent, baseMessage + question, Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } String mainMessage; if (selectedAddOnsJavaIssue.size() == size) { mainMessage = Constant.messages.getString("cfu.confirmation.dialogue.message.addOnsNewerJavaVersion"); } else { mainMessage = Constant.messages.getString("cfu.confirmation.dialogue.message.someSelectedAddOnsNewerJavaVersion"); } JLabel label = new JLabel( Constant.messages.getString("cfu.confirmation.dialogue.message.warnAddOnsNotRunJavaVersion"), ManageAddOnsDialog.ICON_ADD_ON_ISSUES, SwingConstants.LEADING); Object[] msgs = { mainMessage, createScrollableTable(new AddOnTableModel(selectedAddOnsJavaIssue, selectedAddOnsJavaIssue.size())), label, question }; return JOptionPane.showConfirmDialog(parent, msgs, Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } JPanel panel = new JPanel(new BorderLayout()); JTabbedPane tabs = new JTabbedPane(); panel.add(tabs); int issues = selectedAddOnsJavaIssue.size(); if (!selectedAddOnsJavaIssue.isEmpty()) { tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.selectedAddOns"), createScrollableTable(new AddOnTableModel(selectedAddOnsJavaIssue, selectedAddOnsJavaIssue.size()))); } if (!changesResult.getUninstalls().isEmpty()) { tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.uninstallations"), createScrollableTable(new AddOnTableModel(changesResult.getUninstalls(), false))); } if (!updates.isEmpty()) { AddOnTableModel model = new AddOnTableModel(updates); issues += model.getMinimumJavaVersionIssues(); tabs.add(Constant.messages.getString("cfu.confirmation.dialogue.tab.header.updats"), createScrollableTable(model)); } if (!installs.isEmpty()) { AddOnTableModel model = new AddOnTableModel(installs); issues += model.getMinimumJavaVersionIssues(); tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.installations"), createScrollableTable(model)); } if (!dependents.isEmpty()) { AddOnTableModel model = new AddOnTableModel(dependents); issues += model.getMinimumJavaVersionIssues(); tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.softUninstalls"), createScrollableTable(model)); } SelectableAddOnTableModel optionalAddOnsTableModel = null; if (!changesResult.getOptionalAddOns().isEmpty()) { optionalAddOnsTableModel = new SelectableAddOnTableModel(changesResult.getOptionalAddOns()); issues += optionalAddOnsTableModel.getMinimumJavaVersionIssues(); tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.optionalAddOns"), createScrollableTable(optionalAddOnsTableModel)); } if (!changesResult.getUnloadExtensions().isEmpty()) { ExtensionsTableModel model = new ExtensionsTableModel(changesResult.getUnloadExtensions()); tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.extensionUnloads"), createScrollableTable(model)); } if (!changesResult.getSoftUnloadExtensions().isEmpty()) { ExtensionsTableModel model = new ExtensionsTableModel(changesResult.getSoftUnloadExtensions()); tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.extensionSoftUnloads"), createScrollableTable(model)); } List<Object> optionPaneContents = new ArrayList<>(); if (!changesResult.getOptionalAddOns().isEmpty() && changesResult.getUninstalls().isEmpty() && updates.isEmpty() && installs.isEmpty() && dependents.isEmpty() && changesResult.getSoftUnloadExtensions().isEmpty() && changesResult.getUnloadExtensions().isEmpty()) { optionPaneContents.add(Constant.messages.getString("cfu.confirmation.dialogue.message.suggestedChanges")); } else if (!changesResult.getOptionalAddOns().isEmpty()) { optionPaneContents.add(Constant.messages.getString("cfu.confirmation.dialogue.message.requiredSuggestedChanges")); } else { optionPaneContents.add(Constant.messages.getString("cfu.confirmation.dialogue.message.requiredChanges")); } optionPaneContents.add(panel); if (issues != 0) { String message; if (selectedAddOnsJavaIssue.size() == issues) { if (selectedAddOnsJavaIssue.size() == changesResult.getSelectedAddOns().size()) { message = Constant.messages.getString("cfu.confirmation.dialogue.message.selectedAddOnsNewerJavaVersion"); } else { message = Constant.messages.getString("cfu.confirmation.dialogue.message.someUnnamedSelectedAddOnsNewerJavaVersion"); } } else if (issues == 1) { message = Constant.messages.getString("cfu.confirmation.dialogue.message.addOnNewerJavaVersion"); } else { message = Constant.messages.getString("cfu.confirmation.dialogue.message.someAddOnsNewerJavaVersion"); } JLabel label = new JLabel( Constant.messages.getString("cfu.confirmation.dialogue.message.warnUnknownNumberAddOnsNotRunJavaVersion"), ManageAddOnsDialog.ICON_ADD_ON_ISSUES, SwingConstants.LEADING); optionPaneContents.add(message); optionPaneContents.add(label); } optionPaneContents.add(question); boolean confirmed = JOptionPane.showConfirmDialog( parent, optionPaneContents.toArray(), Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; if (confirmed && optionalAddOnsTableModel != null) { changesResult.getInstalls().addAll(optionalAddOnsTableModel.getSelectedAddOns()); } return confirmed; } private Set<AddOn> getDependents(Set<AddOn> updates, Set<AddOn> ignoreAddOns) { Set<AddOn> dependents = new HashSet<>(); for (AddOn update : updates) { addDependents(dependents, update, ignoreAddOns); } return dependents; } private void addDependents(Set<AddOn> dependents, AddOn addOn, Set<AddOn> ignoreAddOns) { for (AddOn availableAddOn : installedAddOns.getInstalledAddOns()) { if (!ignoreAddOns.contains(availableAddOn) && availableAddOn.dependsOn(addOn) && dependents.contains(availableAddOn)) { dependents.add(availableAddOn); addDependents(dependents, availableAddOn, ignoreAddOns); } } } private static JScrollPane createScrollableTable(TableModel model) { JXTable table = new JXTable(model); table.setColumnControlVisible(true); table.setVisibleRowCount(Math.min(model.getRowCount() + 1, 5)); table.packAll(); return new JScrollPane(table); } /** * Calculates the changes required to update the given add-on. * <p> * It might require updating, installing or uninstalling other add-ons depending on the dependencies of the affected * add-ons. * * @param addOn the add-on that would be updated * @return the resulting changes with the add-ons that need to be updated, installed or uninstalled * @since 2.4.3 */ public AddOnChangesResult calculateUpdateChanges(AddOn addOn) { Set<AddOn> addOns = new HashSet<>(); addOns.add(addOn); return calculateUpdateChanges(addOns); } /** * Calculates the changes required to update the given add-ons. * <p> * It might require updating, installing or uninstalling other add-ons depending on the dependencies of the affected * add-ons. * * @param addOns the add-ons that would be updated * @return the resulting changes with the add-ons that need to be updated, installed or uninstalled */ public AddOnChangesResult calculateUpdateChanges(Set<AddOn> addOns) { return calculateChanges(addOns, true); } /** * Asks the user for confirmation of update changes. * * @param parent the parent component of the confirmation dialogue * @param changes the changes of the update * @return {@code true} if the user accept the changes, {@code false} otherwise */ public boolean confirmUpdateChanges(Component parent, AddOnChangesResult changes) { return confirmChanges(parent, changes, true); } private AddOnChangesResult calculateChanges(Set<AddOn> selectedAddOns, boolean updating) { Set<AddOn> oldVersions = new HashSet<>(); Set<AddOn> uninstalls = new HashSet<>(); Set<AddOn> newVersions = new HashSet<>(); Set<AddOn> installs = new HashSet<>(); if (updating) { for (AddOn update : selectedAddOns) { AddOn oldVersion = installedAddOns.getAddOn(update.getId()); oldVersions.add(oldVersion); } } boolean newerJavaVersion = false; for (AddOn addOn : selectedAddOns) { newerJavaVersion |= addDependencies(addOn, selectedAddOns, oldVersions, newVersions, installs); } Set<AddOn> remainingInstalledAddOns = new HashSet<>(); for (AddOn addOn : installedAddOns.getAddOns()) { if (!contains(selectedAddOns, addOn) && !contains(newVersions, addOn)) { remainingInstalledAddOns.add(addOn); } } Set<AddOn> expectedInstalledAddOns = new HashSet<>(remainingInstalledAddOns); expectedInstalledAddOns.addAll(selectedAddOns); expectedInstalledAddOns.addAll(installs); expectedInstalledAddOns.addAll(newVersions); for (AddOn addOn : remainingInstalledAddOns) { if (addOn.calculateRunRequirements(expectedInstalledAddOns).hasDependencyIssue()) { uninstalls.add(addOn); } } for (Iterator<AddOn> it = uninstalls.iterator(); it.hasNext();) { AddOn addOn = it.next(); AddOn addOnUpdate = availableAddOns.getAddOn(addOn.getId()); if (addOnUpdate != null && !addOnUpdate.equals(addOn)) { it.remove(); oldVersions.add(addOn); newVersions.add(addOnUpdate); newerJavaVersion |= addDependencies(addOnUpdate, selectedAddOns, oldVersions, newVersions, installs); } } for (Iterator<AddOn> it = uninstalls.iterator(); it.hasNext();) { AddOn addOn = it.next(); if (contains(installs, addOn) || contains(newVersions, addOn) || (addOn.calculateRunRequirements(installedAddOns.getAddOns()).hasDependencyIssue() && !containsAny( addOn.getIdsAddOnDependencies(), uninstalls))) { it.remove(); } } if (updating) { newVersions.addAll(selectedAddOns); } else { installs.addAll(selectedAddOns); } expectedInstalledAddOns = new HashSet<>(remainingInstalledAddOns); expectedInstalledAddOns.removeAll(uninstalls); expectedInstalledAddOns.removeAll(oldVersions); expectedInstalledAddOns.addAll(installs); expectedInstalledAddOns.addAll(newVersions); Set<Extension> unloadExtensions = new HashSet<>(); Set<Extension> softUnloadExtensions = new HashSet<>(); Set<AddOn> optionalAddOns = new HashSet<>(); for (AddOn addOn : expectedInstalledAddOns) { List<String> extensionsWithDeps = addOn.getExtensionsWithDeps(); for (Extension extension : addOn.getLoadedExtensionsWithDeps()) { AddOn.AddOnRunRequirements requirements = addOn.calculateExtensionRunRequirements( extension, expectedInstalledAddOns); AddOn.ExtensionRunRequirements extReqs = requirements.getExtensionRequirements().get(0); if (!extReqs.isRunnable()) { unloadExtensions.add(extension); } else if (CollectionUtils.containsAny(extReqs.getDependencies(), oldVersions)) { softUnloadExtensions.add(extension); } extensionsWithDeps.remove(extReqs.getClassname()); } for (String classname : extensionsWithDeps) { AddOn.AddOnRunRequirements requirements = addOn.calculateExtensionRunRequirements( classname, availableAddOns.getAddOns()); AddOn.ExtensionRunRequirements extReqs = requirements.getExtensionRequirements().get(0); if (extReqs.isRunnable()) { optionalAddOns.addAll(extReqs.getDependencies()); } } } optionalAddOns.removeAll(installs); optionalAddOns.removeAll(newVersions); optionalAddOns.removeAll(remainingInstalledAddOns); return new AddOnChangesResult( selectedAddOns, oldVersions, uninstalls, newVersions, installs, newerJavaVersion, optionalAddOns, unloadExtensions, softUnloadExtensions); } /** * Calculates the changes required to uninstall the given add-ons. * <p> * It might require uninstalling other add-ons depending on the dependencies of the affected add-ons. * * @param selectedAddOns the add-ons that would be uninstalled * @return the resulting changes with the add-ons that need to be uninstalled */ public UninstallationResult calculateUninstallChanges(Set<AddOn> selectedAddOns) { List<AddOn> remainingAddOns = new ArrayList<>(installedAddOns.getAddOns()); remainingAddOns.removeAll(selectedAddOns); Set<AddOn> uninstallations = new HashSet<>(); List<AddOn> addOnsToCheck = new ArrayList<>(remainingAddOns); while (!addOnsToCheck.isEmpty()) { AddOn addOn = addOnsToCheck.remove(0); AddOn.AddOnRunRequirements requirements = addOn.calculateRunRequirements(remainingAddOns); if (!requirements.hasDependencyIssue()) { addOnsToCheck.removeAll(requirements.getDependencies()); } else if (AddOn.InstallationStatus.UNINSTALLATION_FAILED != addOn.getInstallationStatus()) { uninstallations.add(addOn); } } for (Iterator<AddOn> it = uninstallations.iterator(); it.hasNext();) { AddOn addOn = it.next(); if (addOn.calculateRunRequirements(installedAddOns.getAddOns()).hasDependencyIssue() && !containsAny(addOn.getIdsAddOnDependencies(), uninstallations)) { it.remove(); } } remainingAddOns.removeAll(uninstallations); Set<Extension> extensions = new HashSet<>(); for (AddOn addOn : remainingAddOns) { if (addOn.hasExtensionsWithDeps()) { for (Extension ext : addOn.getLoadedExtensions()) { AddOn.AddOnRunRequirements requirements = addOn.calculateExtensionRunRequirements(ext, remainingAddOns); if (!requirements.getExtensionRequirements().isEmpty()) { AddOn.ExtensionRunRequirements extReqs = requirements.getExtensionRequirements().get(0); if (!extReqs.isRunnable()) { extensions.add(ext); } } } } } uninstallations.addAll(selectedAddOns); return new UninstallationResult(selectedAddOns, uninstallations, extensions); } private boolean containsAny(List<String> addOnIds, Collection<AddOn> addOns) { for (String id : addOnIds) { for (AddOn addOn : addOns) { if (id.equals(addOn.getId())) { return true; } } } return false; } /** * Asks the user for confirmation of uninstall changes. * <p> * User will also be warned about add-ons that are being uninstalled which are required by add-ons being downloaded. * * @param parent the parent component of the confirmation dialogue * @param result the calculation result of the uninstallation * @param addOnsBeingDownloaded the add-ons being downloaded to check if depend on the add-ons being uninstalled * @return {@code true} if the user accept the changes, {@code false} otherwise */ public boolean confirmUninstallChanges(Component parent, UninstallationResult result, Set<AddOn> addOnsBeingDownloaded) { Set<AddOn> forcedUninstallations = new HashSet<>(result.getUninstallations()); forcedUninstallations.removeAll(result.getSelectedAddOns()); boolean dependencyDownloadFound = false; for (AddOn addOnDownloading : addOnsBeingDownloaded) { if (containsAny(addOnDownloading.getIdsAddOnDependencies(), forcedUninstallations)) { dependencyDownloadFound = true; break; } } if (!dependencyDownloadFound) { for (AddOn addOnDownloading : addOnsBeingDownloaded) { if (containsAny(addOnDownloading.getIdsAddOnDependencies(), result.getSelectedAddOns())) { dependencyDownloadFound = true; break; } } } if (dependencyDownloadFound) { if (JOptionPane.showConfirmDialog( parent, new Object[] { Constant.messages.getString("cfu.confirmation.dialogue.message.uninstallsRequiredByAddOnsDownloading"), Constant.messages.getString("cfu.confirmation.dialogue.message.continueWithUninstallation") }, Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { return false; } } if (forcedUninstallations.isEmpty() && result.getExtensions().isEmpty()) { return JOptionPane.showConfirmDialog( parent, Constant.messages.getString("cfu.uninstall.confirm"), Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } if (result.getExtensions().isEmpty()) { return JOptionPane.showConfirmDialog( parent, new Object[] { Constant.messages.getString("cfu.uninstall.dependentAddOns.confirm"), createScrollableTable(new AddOnTableModel(forcedUninstallations, false)), Constant.messages.getString("cfu.confirmation.dialogue.message.continueWithUninstallation") }, Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } if (forcedUninstallations.isEmpty()) { return JOptionPane.showConfirmDialog( parent, new Object[] { Constant.messages.getString("cfu.uninstall.dependentExtensions.confirm"), createScrollableTable(new ExtensionsTableModel(result.getExtensions())), Constant.messages.getString("cfu.confirmation.dialogue.message.continueWithUninstallation") }, Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } JPanel panel = new JPanel(new BorderLayout()); JTabbedPane tabs = new JTabbedPane(); panel.add(tabs); tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.uninstallations"), createScrollableTable(new AddOnTableModel(forcedUninstallations, false))); tabs.add( Constant.messages.getString("cfu.confirmation.dialogue.tab.header.extensionUnloads"), createScrollableTable(new ExtensionsTableModel(result.getExtensions()))); List<Object> optionPaneContents = new ArrayList<>(); optionPaneContents.add(Constant.messages.getString("cfu.uninstall.dependentAddonsAndExtensions.confirm")); optionPaneContents.add(panel); optionPaneContents.add(Constant.messages.getString("cfu.confirmation.dialogue.message.continueWithUninstallation")); return JOptionPane.showConfirmDialog( parent, optionPaneContents.toArray(), Constant.PROGRAM_NAME, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } /** * The result of an installation or update of add-on(s). * <p> * Contains all add-ons that need to be installed, updated and uninstalled as the result of the changes. */ public static class AddOnChangesResult { private final Set<AddOn> selectedAddOns; private final Set<AddOn> oldVersions; private final Set<AddOn> uninstalls; private final Set<AddOn> newVersions; private final Set<AddOn> installs; private final boolean newerJavaVersion; private final Set<AddOn> optionalAddOns; private final Set<Extension> unloadExtensions; private final Set<Extension> softUnloadExtensions; private AddOnChangesResult( Set<AddOn> selectedAddOns, Set<AddOn> oldVersions, Set<AddOn> uninstalls, Set<AddOn> newVersions, Set<AddOn> installs, boolean newerJavaVersion, Set<AddOn> optionalAddOns, Set<Extension> unloadExtensions, Set<Extension> softUnloadExtensions) { this.selectedAddOns = selectedAddOns; this.oldVersions = oldVersions; this.uninstalls = uninstalls; this.newVersions = newVersions; this.installs = installs; this.newerJavaVersion = newerJavaVersion; this.optionalAddOns = optionalAddOns; this.unloadExtensions = unloadExtensions; this.softUnloadExtensions = softUnloadExtensions; } /** * Gets the add-ons selected for installation or updated. * * @return the add-ons selected for installation or updated */ public Set<AddOn> getSelectedAddOns() { return selectedAddOns; } /** * Gets old versions of the add-ons that need to be updated as result of the changes. * * @return the old versions of the add-ons that need to be updated */ public Set<AddOn> getOldVersions() { return oldVersions; } /** * Gets the add-ons that need to be uninstalled as result of the changes. * * @return the the add-ons that need to be uninstalled */ public Set<AddOn> getUninstalls() { return uninstalls; } /** * Gets the new versions of add-ons that need to be updated as result of the changes. * * @return the new versions of the add-ons */ public Set<AddOn> getNewVersions() { return newVersions; } /** * Gets the add-ons that need to be installed as result of the changes. * * @return the add-ons that need to be installed * @see #getNewVersions() */ public Set<AddOn> getInstalls() { return installs; } /** * Tells whether or not a newer Java version is required by any of add-ons as result of the changes. * * @return {@code true} if a newer Java version is required by any of the add-ons, {@code false} otherwise */ public boolean isNewerJavaVersionRequired() { return newerJavaVersion; } /** * Gets the optional add-ons. * * @return the optional add-ons */ public Set<AddOn> getOptionalAddOns() { return optionalAddOns; } /** * Gets the extensions that have to be unloaded as result of the changes. * * @return the extensions that have to be unloaded */ public Set<Extension> getUnloadExtensions() { return unloadExtensions; } /** * Gets the extensions that have to be soft unloaded as result of the changes. The extension will be unloaded and then * loaded once the dependency is updated- * * @return the extensions that have to be soft unloaded */ public Set<Extension> getSoftUnloadExtensions() { return softUnloadExtensions; } /** * Add the contents from the specified results * @param result */ public void addResults(AddOnChangesResult result) { selectedAddOns.addAll(result.getSelectedAddOns()); oldVersions.addAll(result.oldVersions); uninstalls.addAll(result.getUninstalls()); newVersions.addAll(result.getNewVersions()); installs.addAll(result.getInstalls()); optionalAddOns.addAll(result.getOptionalAddOns()); unloadExtensions.addAll(result.getUnloadExtensions()); softUnloadExtensions.addAll(result.getSoftUnloadExtensions()); } } /** * The result of an uninstallation of add-on(s). * <p> * Contains all add-ons that need to be uninstalled as the result of an uninstallation or several. */ public static class UninstallationResult { private final Set<AddOn> selectedAddOns; private final Set<AddOn> uninstallations; private final Set<Extension> extensions; private UninstallationResult(Set<AddOn> selectedAddOns, Set<AddOn> uninstallations, Set<Extension> extensions) { this.selectedAddOns = selectedAddOns; this.uninstallations = uninstallations; this.extensions = extensions; } /** * Gets the add-ons selected for uninstallation. * * @return the add-ons selected for uninstallation */ public Set<AddOn> getSelectedAddOns() { return selectedAddOns; } /** * Gets all the add-ons (selected and dependencies) that need to be uninstalled as result of the uninstallations. * * @return all the add-ons that need to be uninstalled */ public Set<AddOn> getUninstallations() { return uninstallations; } /** * Gets the extensions that are affected by the changes, but not its add-on. * * @return the extensions affected by the changes */ public Set<Extension> getExtensions() { return extensions; } } private static class AddOnTableModel extends AbstractTableModel { private static final long serialVersionUID = 5446781970087315105L; private static final String[] COLUMNS = { Constant.messages.getString("cfu.generic.table.header.addOn"), Constant.messages.getString("cfu.generic.table.header.version"), Constant.messages.getString("cfu.generic.table.header.minimumJavaVersion") }; private final List<AddOn> addOns; private final int columnCount; private final int issues; public AddOnTableModel(Collection<AddOn> addOns) { this(addOns, true); } public AddOnTableModel(Collection<AddOn> addOns, boolean checkMinimumJavaVersion) { this.addOns = new ArrayList<>(addOns); int count = 0; if (checkMinimumJavaVersion) { for (AddOn addOn : addOns) { if (!addOn.canRunInCurrentJavaVersion()) { ++count; } } } issues = count; columnCount = issues != 0 ? COLUMNS.length : COLUMNS.length - 1; } public AddOnTableModel(Collection<AddOn> addOns, int numberOfIssues) { this.addOns = new ArrayList<>(addOns); issues = numberOfIssues; columnCount = numberOfIssues != 0 ? COLUMNS.length : COLUMNS.length - 1; } public int getMinimumJavaVersionIssues() { return issues; } @Override public String getColumnName(int column) { return COLUMNS[column]; } @Override public int getColumnCount() { return columnCount; } @Override public int getRowCount() { return addOns.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { AddOn addOn = getAddOn(rowIndex); switch (columnIndex) { case 0: return addOn.getName(); case 1: return Integer.valueOf(addOn.getFileVersion()); case 2: return addOn.getMinimumJavaVersion(); default: return ""; } } protected AddOn getAddOn(int rowIndex) { return addOns.get(rowIndex); } } private static class SelectableAddOnTableModel extends AddOnTableModel { private static final long serialVersionUID = 2337381848530495407L; private final Boolean[] selections; public SelectableAddOnTableModel(Collection<AddOn> addOns) { super(addOns, true); selections = new Boolean[addOns.size()]; for (int i = 0; i < selections.length; i++) { selections[i] = Boolean.FALSE; } } @Override public String getColumnName(int column) { if (column == 0) { return ""; } return super.getColumnName(column - 1); } @Override public int getColumnCount() { return super.getColumnCount() + 1; } @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 0) { return Boolean.class; } return super.getColumnClass(columnIndex - 1); } @Override public Object getValueAt(int rowIndex, int columnIndex) { if (columnIndex == 0) { return selections[rowIndex]; } return super.getValueAt(rowIndex, columnIndex - 1); } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return columnIndex == 0; } @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { if (columnIndex == 0 && aValue instanceof Boolean) { selections[rowIndex] = (Boolean) aValue; fireTableCellUpdated(rowIndex, columnIndex); } } public List<AddOn> getSelectedAddOns() { List<AddOn> selectedAddOns = new ArrayList<>(selections.length); for (int i = 0; i < selections.length; i++) { if (selections[i].booleanValue()) { selectedAddOns.add(getAddOn(i)); } } return selectedAddOns; } } private static class ExtensionsTableModel extends AbstractTableModel { private static final long serialVersionUID = 5446781970087315105L; private static final String[] COLUMNS = { Constant.messages.getString("cfu.generic.table.header.extension") }; private final List<Extension> extensions; public ExtensionsTableModel(Collection<Extension> extensions) { this.extensions = new ArrayList<>(extensions); } @Override public String getColumnName(int column) { return COLUMNS[column]; } @Override public int getColumnCount() { return COLUMNS.length; } @Override public int getRowCount() { return extensions.size(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { Extension extension = extensions.get(rowIndex); switch (columnIndex) { case 0: return extension.getName(); default: return ""; } } } }